如何显式转换 'object' 类型的变量以满足多类型泛型约束?
How can you explicitly cast a variable of type 'object' to satisfy a multi-typed generic constraint?
我们有一个通用扩展方法,它仅适用于同时支持 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();
那么如何将一个对象转换为多个接口以便调用泛型?甚至不确定这是否可能。
我能想到的最简洁的方法是 dynamic
。但是,由于扩展方法不能很好地与 dynamic
配合使用(见下文),您必须显式引用扩展方法。
Extensions.DoSomething(unknownType as dynamic);
EDIT 作为对我 运行 的陷阱的警告,请注意使用显式 dyanmic
作为类型参数调用方法(通过 DoSomething<dynamic>
) 将 not 工作 - 它会在尝试匹配多个约束时导致编译错误。此外,当不使用多个约束时,这会导致 dynamic
根据传递的变量的编译时类型进行解析, 而不是 运行时类型。
这将导致调用 Extensions.UnknownType<dynamic>
,其中的 dynamic
将在运行时解析 - 这意味着它将使用给定参数的完全派生类型。只要这个参数实现了所需的接口,就可以了。
请注意,与许多 dynamic
代码一样,这可能会遇到直到运行时才会出现的问题。谨慎使用!
如果您使用相同的通用参数进行多次调用,您最好在转换器中添加一个通用辅助方法,然后使用 value as dynamic
[调用 that
附录:
使用 dynamic
时,针对 dynamic
对象调用的任何内容都将尝试在运行时解析为给定类型的成员,但 不会 查找扩展方法,因为它们本质上存在于完全不同的 class 中,通常是不同的程序集。
您可以尝试使用 MethodInfo 和委托来检索对正确方法的委托。
以下解决方案不使用动态,但使用了一点反射。
该方法基本上执行以下操作:
- 检索您要调用的方法的委托,但为另一个已知类型。
- 从中检索泛型方法定义。
- 然后检索对象类型的 MethodInfo。
- 通过调用最终的 MethodInfo.
来调用扩展方法
_Placeholder class 是用于获取初始委托的已知类型,但您可以使用满足通用约束的任何其他类型。
这样您还可以在编译时检查扩展方法的名称及其限制。
一个好的方法还可以缓存最终的 MethodInfo(或委托),以避免重复昂贵的调用。
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);
}
}
我们有一个通用扩展方法,它仅适用于同时支持 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();
那么如何将一个对象转换为多个接口以便调用泛型?甚至不确定这是否可能。
我能想到的最简洁的方法是 dynamic
。但是,由于扩展方法不能很好地与 dynamic
配合使用(见下文),您必须显式引用扩展方法。
Extensions.DoSomething(unknownType as dynamic);
EDIT 作为对我 运行 的陷阱的警告,请注意使用显式 dyanmic
作为类型参数调用方法(通过 DoSomething<dynamic>
) 将 not 工作 - 它会在尝试匹配多个约束时导致编译错误。此外,当不使用多个约束时,这会导致 dynamic
根据传递的变量的编译时类型进行解析, 而不是 运行时类型。
这将导致调用 Extensions.UnknownType<dynamic>
,其中的 dynamic
将在运行时解析 - 这意味着它将使用给定参数的完全派生类型。只要这个参数实现了所需的接口,就可以了。
请注意,与许多 dynamic
代码一样,这可能会遇到直到运行时才会出现的问题。谨慎使用!
如果您使用相同的通用参数进行多次调用,您最好在转换器中添加一个通用辅助方法,然后使用 value as dynamic
[调用 that
附录:
使用 dynamic
时,针对 dynamic
对象调用的任何内容都将尝试在运行时解析为给定类型的成员,但 不会 查找扩展方法,因为它们本质上存在于完全不同的 class 中,通常是不同的程序集。
您可以尝试使用 MethodInfo 和委托来检索对正确方法的委托。
以下解决方案不使用动态,但使用了一点反射。 该方法基本上执行以下操作:
- 检索您要调用的方法的委托,但为另一个已知类型。
- 从中检索泛型方法定义。
- 然后检索对象类型的 MethodInfo。
- 通过调用最终的 MethodInfo. 来调用扩展方法
_Placeholder class 是用于获取初始委托的已知类型,但您可以使用满足通用约束的任何其他类型。
这样您还可以在编译时检查扩展方法的名称及其限制。
一个好的方法还可以缓存最终的 MethodInfo(或委托),以避免重复昂贵的调用。
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);
}
}