如何显式地转换'object'类型的变量以满足多类型泛型约束?

时间:2015-05-11 22:26:44

标签: c# generics casting

我们有一个通用的扩展方法,它只适用于支持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();

那么如何将对象转换为多个接口以便调用泛型呢?甚至不确定它是否可能。

2 个答案:

答案 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 。 基本上该方法执行以下操作:

  1. 检索要调用的方法的委托,但是另一个已知类型的委托。
  2. 从中检索通用方法定义。
  3. 然后检索对象类型的 MethodInfo
  4. 通过调用最终的 MethodInfo
  5. 来调用扩展方法

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