C# Generics - Resolved - Как решить неоднозначную ошибку компилятора, вызывая метод переданного универсального типа; например, IXList.Head() и IYList.Head()

Акцент в моем вопросе сделан на C# Generics и передаче интерфейса IXList -или-IYList этому методу GetAllValues (). Целью является общий код для вызова GetAllValues<IXList>() -или же- GetAllValues<IYList>(),

09-21-13 Продолжение: я узнал, что: where Z : IXList, IYList означает, что Z имеет тип IXList а также ITList - при таком понимании можно попробовать where Z : class - это в лучшем случае боковой шаг все еще не работает, потому что тогда Z.anything... anything не найден, компилятор не в курсе Z...

Может ли быть проверен фактический тип пройденного Z и затем правильно выбрана условная выборка кода во время выполнения list вызвать.Head(), .Get() и.Next() соответственно? например, ((IXList)list).Head(); -или же- ((IYList)list).Head(); [[Да, это возможно, см. 2-й ответ с кодом ниже от 09-24-13.]]

09-29-13 Последующие действия: И наконец, ответ / решение № 3, опубликованные ниже, перемещает окончательное решение к более объектно-ориентированному. И, таким образом, это 3-е решение ставит под сомнение мой первоначальный вопрос о том, как задать тип универсального и как решить ошибку неоднозначности компилятора, возникшую изначально. [[См. Ответ / решение № 3 с кодом ниже от 09-29-13.]]

    internal static IEnumerable<int> GetAllValues<Z>(Z list)
        where Z : IXList, IYList
    {
        try
        {
            list.Head();        // Error 344 - The call is ambiguous between the following
                                // methods or properties: 'IXList.Head()' and 'IYList.Head()'   
        }
        catch (Exception)
        {
            yield break;
        }

        do
        {
            IntPtr lineItemPointer;
            int x = list.Get();     // Error 345 - The call is ambiguous between the following
                                    // methods or properties: 'IXList.Get()' and 'IYList.Get()' 

            yield return x;

            try
            {
                list.Next();        // Error 346 -The call is ambiguous between the following
                                    // methods or properties: 'IXList.Next()' and 'IYList.Next()'   
            }
            catch (Exception)
            {
                yield break;
            }
        } while (true);
    } 

3 ответа

Решение

Решение № 3: безусловно лучшее, не нужно ничего разыгрывать, не нужно спрашивать тип входного списка (многократно), намного чище. Пожалуйста, сравните с ответом / решением № 2 выше.

(# 3) выполняет именно ту цель, которую я искал, общий код для логики, которая обрабатывает либо IXList, либо IYList. И в качестве метода расширения пользователь получает желаемую версию "GetAllItems"...

    internal static IEnumerable<IXOutList> GetAllLineItems(this IXList list)
    {
        TListEnumeratorBase genList = new IXListEnum(list);
        return GetAllLineItemsGeneric<IXOutList>(genList);
    }


    internal static IEnumerable<IYOutList> GetAllLineItems(this IYList list)
    {
        TListEnumeratorBase genList = new IYListEnum(list);
        return GetAllLineItemsGeneric<IYOutList>(genList);
    }                                                                                                                                   


    private static IEnumerable<Zout> GetAllLineItemsGeneric<Zout>(TListEnumeratorBase genList)
        where Zout : class     
    {
        try
        {
            genList.Head();
        }
        catch (Exception)
        {
            yield break;
        }

        Guid guid = Marshal.GenerateGuidForType(typeof(Zout));

        do
        {
            Zout rec = null;
            try
            {
                IntPtr lineItemPointer;
                lineItemPointer = genList.GetTxnItem(ref guid);
                rec = Marshal.GetObjectForIUnknown(lineItemPointer) as Zout;
            }
            catch (Exception)
            {
                yield break;
            }

            if (rec != null)
                yield return rec;
            else
                yield break;

            try
            {
                genList.Next();
            }
            catch (Exception)
            {
                yield break;
            }
        } while (true);
    }

    public abstract class TListEnumeratorBase
    {
        public abstract void Head();
        public abstract void Next();
        public abstract IntPtr GetTxnItem(ref Guid guid);
    }

    public class IXListEnum : TListEnumeratorBase
    {
        IXList _list;
        public IXListEnum(IXList list)
        {
            _list = list;
        }
        public override void Head()
        {
            _list.Head();
        }
        public override void Next()
        {
            _list.Next();
        }
        public override IntPtr GetTxnItem(ref Guid guid)
        {
            IntPtr lineItemPointer;
            _list.Get(ref guid, out lineItemPointer);
            return lineItemPointer;
        }
    }

    public class IYListEnum : TListEnumeratorBase
    {
        IYList _list;
        public IYListEnum(IYList list)
        {
            _list = list;
        }
        public override void Head()
        {
            _list.Head();
        }
        public override void Next()
        {
            _list.Next();
        }
        public override IntPtr GetTxnItem(ref Guid guid)
        {
            IntPtr lineItemPointer;
            _list.Get(ref guid, out lineItemPointer);
            return lineItemPointer;
        }
    }

Поскольку ваш код теперь является вашим шаблоном, требуется, чтобы Z был классом, а также IXList и IYList, чтобы вы не достигли своей цели с помощью этого кода. Для этого вам нужен общий интерфейс для IXList и IYList, как вы можете увидеть ниже IMyList. Если вы введете это, вы просто измените оператор where на:

where Z : class, IMyList 

рассмотреть, даже если вам действительно нужно ограничение класса.


Что касается двусмысленности:

Если вы можете изменить интерфейсы IXList, IYList, то вы можете исключить эти общие части, чтобы отделить одну и наследовать от новой:

interface IMyList
{
    void Head();
    //....
}

inteface  IXList :IMyList { //....
inteface  IYList :IMyList { //....

Это разрешит неоднозначность


Если по какой-либо причине вы не можете этого сделать, ваш единственный вариант - использовать один из ваших интерфейсов при каждом вызове.

((IXList)list).Head();

это решит двусмысленность, но это не то, что вы хотели в первую очередь. Если вы не можете ввести новый интерфейс, то единственный вариант иметь этот общий вид кода - реализовать этот метод расширения для каждого вида списка.

Следующее точно выполняет цель, которую я искал, общий код для логики, которая обрабатывает либо IXList, либо IYList. Да!

И в качестве метода расширения пользователь получает желаемую версию "GetAllItems"...

var landRecs = _land.MainXList().GetAllItems();

var landAltRecs = _land.AlternateYList().GetAllItems();

    internal static IEnumerable<Iint> GetAllItems(this IXList list)
    {
        return GetAllItemsGeneric<Iint, IXList>(list);
    }

    internal static IEnumerable<string> GetAllItems(this IYList list)
    {
        return GetAllItemsGeneric<string, IYList>(list);
    }                                                                                                                                   

    private static IEnumerable<Zout> GetAllItemsGeneric<Zout, T>(T list)
        where Zout : class      // (string -or- int)  -or-  (IReadOnlyTxnMisc or ITxnMisc) etc.
        where T : class         // IYList  -or- IXList  
    {
        try
        {
            DoHeadforList<T>(list);
        }
        catch (Exception)
        {
            yield break;
        }

        Guid guid = Marshal.GenerateGuidForType(typeof(Zout));

        do
        {
            Zout rec = null;
            try
            {
                IntPtr itemPointer;
                itemPointer = DoGetItemforList<T>(list, ref guid);
                rec = Marshal.GetObjectForIUnknown(itemPointer) as Zout;
            }
            catch (Exception)
            {
                yield break;
            }

            if (rec != null)
            {
                yield return rec;
            }
            else
            {
                yield break;
            }

            try
            {
                DoNextforList<T>(list);
            }
            catch (Exception)
            {
                yield break;
            }
        } while (true);
    }

    private enum IListType
    {
        X,
        Y,
        Unknown
    }

    private static IListType GetListTypeAsEnum<T>() where T : class
    {
        IListType rtType = IListType.Unknown;      
        Type inListType = typeof(T);

        if (inListType == typeof(IXList))
        {
            rtType = IListType.X;
        }
        else if (inListType == typeof(IYList))
        {
            rtType = IListType.Y;
        }
        return rtType;
    }

    private static IntPtr DoGetItemforList<T>(T list, ref Guid guid) where T : class
    {
        IntPtr itemPointer;
        if (list == null)
        {
            throw new ArgumentNullException("end of list");
        }
        switch (GetListTypeAsEnum<T>())
        {
            case IListType.X:
                ((IXList)list).GetItem(ref guid, out itemPointer);
                break;
            case IListType.Y:
                ((IYList)list).GetItem(ref guid, out itemPointer);
                break;
            default:
                throw new ArgumentException("unknown passed type");
        }
        return itemPointer;
    }

    private static void DoHeadforList<T>(T list) where T : class
    {
        if (list == null)
        {
            throw new ArgumentNullException("end of list");
        }
        switch (GetListTypeAsEnum<T>())
        {
            case IListType.X:
                ((IXList)list).Head();
                break;
            case IListType.Y:
                ((IYList)list).Head();
                break;
            default:
                throw new ArgumentException("unknown passed type");
        }
    }

    private static void DoNextforList<T>(T list) where T : class
    {
        if (list == null)
        {
            throw new ArgumentNullException("end of list");
        }
        switch (GetListTypeAsEnum<T>())
        {
            case IListType.X:
                ((IXList)list).Next();
                break;
            case IListType.Y:
                ((IYList)list).Next();
                break;
            default:
                throw new ArgumentException("unknown passed type");
        }
    }
Другие вопросы по тегам