C# Generics - Resolved - How to solve the ambiguous compiler error calling a method of passed generic type; e.g., IXList.Head() and IYList.Head()

StackOverflow https://stackoverflow.com/questions/18909175

The emphasis of my question is with C# Generics and the passing of the interface IXList -or- IYList to this 'GetAllValues()' method. The goal is common code for calling GetAllValues<IXList>() -or- GetAllValues<IYList>().

09-21-13 Follow up: I have learned that: where Z : IXList, IYList means that Z is of type IXList and ITList -- given this understanding one might try where Z : class - this is at best a lateral step of still not working because then Z.anything ... anything is not found, the compiler is clueless about Z...

Can the actual type of Z passed be tested and then conditional run-time code pick correct casted list to call .Head(), .Get(), and .Next() accordingly? e.g., ((IXList)list).Head(); -or- ((IYList)list).Head(); [[ Yes it is possible, please see 2nd answer with code below dated 09-24-13.]]

09-29-13 Follow up: And finally, answer/solution #3 posted below moves the final solution to a more object oriented one. And thus, this 3rd solution renders moot my initial question on how to ask the type of generic and how to solve the compiler ambiguity error encountered initially. [[ Please see answer/solution #3, with code below dated 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

Solution #3: by-far the best, no need to cast anything, no need to ask the type of the input list (repeatedly), much much cleaner. Please compare with Answer/Solution #2 above.

(#3) accomplishes exactly the goal I was seeking, common code for the logic that process either IXList or IYList. And as an extension method the user gets the 'GetAllItems' typed version desired...

    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;
        }
    }

其他提示

As your code is now your template requires Z to be class AND IXList AND IYList so you will not achieve your goal with this code. To do that you need a common interface for IXList and IYList as you can see below IMyList. If you introduce that you simply change the where statement to:

where Z : class, IMyList 

consider even if you really need the class constraint.


As to ambiguity:

If you can modify interfaces IXList, IYList then you can exclude those common parts to separate one and inherit from the new one:

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

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

This will resolve ambiguity


If for what ever reason you cannot do that your only option is to cast on one of your interfaces on every call

((IXList)list).Head();

this will resolve ambiguity but is not what you wanted in the first place. If you cannot introduce new interface then only option to have this common looking code would be to implement this extension method for each list kind.

The following accomplishes exactly the goal I was seeking, common code for the logic that process either IXList or IYList. Yea!

And as an extension method the user gets the 'GetAllItems' typed version desired...

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");
        }
    }
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top