我们有一个通用的扩展方法,它只适用于支持INotifyCollectionChanged和IEnumerable接口的对象。它是这样写的:
public static class SomeExtensions
{
public static void DoSomething<T>(this T item)
where T : INotifyCollectionChanged, IEnumerable
{
// Do something
}
}
以下编译很好,因为ObservableCollection<string>
实现了两个接口,并且由于'var'关键字,knownType是强类型的:
var knownType = new ObservableCollection<string>();
knownType.DoSomething();
但是,我们正试图从ValueConverter中调用它。问题是传入的值是object
类型,即使我们可以测试传入的对象确实实现了两个接口,我们也无法弄清楚如何显式地转换它,以便我们可以调用该泛型扩展方法。这显然不起作用......
object unknownType = new ObservableCollection<string>();
((INotifyCollectionChanged,IEnumerable)unknownType).DoSomething();
那么如何将对象转换为多个接口以便调用泛型呢?甚至不确定它是否可能。
答案 0 :(得分:2)
我能想到的最简洁的方法是dynamic
。但是,由于扩展方法与dynamic
不一致(请参阅blelow),因此您必须明确引用扩展方法。
Extensions.DoSomething(unknownType as dynamic);
编辑作为警告,我遇到了问题,请注意调用带有显式dyanmic
作为类型参数的方法(通过DoSomething<dynamic>
)不工作 - 尝试匹配多个约束时会导致编译错误。此外,当不使用多个约束时,这会导致dynamic
基于传递的变量的编译时类型解析,不运行时类型。
这将导致调用Extensions.UnknownType<dynamic>
,dynamic
将在运行时解析 - 这意味着它将使用给定参数的完全派生类型。只要此参数实现了所需的接口,就可以了。
要像许多dynamic
代码一样警惕,这可能会遇到直到运行时才能看到的问题。谨慎使用!
如果使用相同的通用参数进行多次调用,最好在转换器中添加通用辅助方法,然后使用value as dynamic
调用
<强>附录强>:
使用dynamic
时,针对dynamic
对象调用的任何内容都将尝试在运行时作为给定类型的成员解析,但不将查找扩展方法,因为它们本质上存在于一个完全不同的类中,通常是不同的集合。
答案 1 :(得分:0)
您可以尝试使用MethodInfo和委托来检索正确方法的委托。
以下解决方案不使用动态,但稍微使用 Reflection 。 基本上该方法执行以下操作:
_Placeholder 类是用于获取初始委托的已知类型,但您可以使用满足通用约束的任何其他类型。
通过这种方式,您还可以对扩展方法的名称及其约束进行编译时检查。
一个好方法也会缓存最终的MethodInfo(或Delegate),以避免重复昂贵的调用。
private sealed class _Placeholder : INotifyCollectionChanged, IEnumerable
{
public event NotifyCollectionChangedEventHandler CollectionChanged;
public IEnumerator GetEnumerator() { throw new NotImplementedException(); }
}
static void Test(object obj)
{
if ((obj is INotifyCollectionChanged) && (obj is IEnumerable))
{
var typeObject = obj.GetType();
// Retrieve the delegate for another type
Action<_Placeholder> _DoSomething = SomeExtensions.DoSomething<_Placeholder>;
// Retrieve the generic definition
var infoTemp = _DoSomething.Method.GetGenericMethodDefinition();
// Retrieve the MethodInfo for our object's type
var method = infoTemp.MakeGenericMethod(typeObject);
// Call the extension method by invoking
method.Invoke(null, new[] { obj });
//If you can return the delegate, you can try the following:
//var typeDelegate = typeof(Action<>).MakeGenericType(typeObject);
//var action = Delegate.CreateDelegate(typeDelegate, method);
//((Action<_Test>)action)(obj as _Test);
}
}