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