将对象同时转换为两个接口,以调用generics方法
我想调用一个约束输入类型T的generics方法来实现两个接口:
interface IA { } interface IB { } void foo(T t) where T : IA, IB { }
我怎样才能修复最后一行
void bar(object obj) { if (obj is IA && obj is IB) { foo((IA && IB)obj); } }
?
反思可能允许拨打电话,但我想留在语言中。
C#4.0动态关键字是否会让您从监狱(大多数)免费? 毕竟 – 你已经在进行类型检查了。
interface IC : IA, IB { } void bar(object obj) { if (obj is IA && obj is IB) { IC x = (dynamic)obj; foo(x); } }
如果foo尝试将参数强制转换为T,那会破坏吗? 我不知道。
您似乎误解了generics的工作原理:当调用具有generics参数T
, T
必须在编译时静态知道。 虽然编译器有时可以推断它(因此您并不总是需要明确地将其写下来),但在调用方法时必须提供一些T
在你的情况下,你所知道的是obj
是一个IA
和一个IB
,但这并没有给你足够的信息来调用foo
,因为你不知道应该是什么。 您必须使用reflection,强制转换为实现IA
和IB
的特定类型,或者进行更具戏剧性的设计更改。
我同意其他响应者的说法,如果你需要这样做,你可能会遇到设计问题,但是你可以用一个实现两个接口的代理对象完成它,并将调用委托给未知Object的两个连接接口实例。 现在,当您调用此方法时,您可以为任何支持这两种接口的类型构建代理。
你的bar
方法也应该是通用的,具有与foo
相同的约束。
但是如果你真的想解决这个问题,可以为一个对象创建一个包装器,该对象实现两个接口,这些接口委托对包装实例的所有调用:
class ABWrapper : IA, IB { IA _a; IB _b; public Wrapper(IA a) { if (!(a is IB)) throw new ArgumentException(); _a = a; _b = (IB)a; } public Wrapper(IB b) { if (!(b is IA)) throw new ArgumentException(); _a = (IA)b; _b = b; } // explicit implementation for IA and IB delegating to _a and _b }
并像这样使用它:
static void bar(object obj) { if (obj is IA && obj is IB) { foo(new ABWrapper((IA)obj)); } }
我不是在宽恕这种做法,但这里有两个选项,其他人没有提到:
如果你能控制foo
然后你可以将它重构为:
void foo(IA asA, IB asB) { if (!ReferenceEquals(isA, isB)) throw new ArgumentException("isA and isB must be the same object"); // Your code here }
这允许您的调用代码变为:
foo(obj as IA, obj as IB);
它不漂亮,但它可能是一个选择。
如果你喜欢大流行的黑客攻击
如果你发现自己需要做很多事情,那就是难闻的气味而且有更好的设计。
但是如果你没有选择并且这是“需要”到处都是,那么这可能会让你的生活更轻松,因为你不必浪费时间创建实现你的IA
和IB
接口的类:
/// /// An ugly hack for when you don't want to create a new wrapper class that inherits from and implements two other interfaces /// /// /// public sealed class MultiType { /// /// The contained item /// private readonly object _containedObject; /// /// The contained item as a TOne /// public TOne AsOne => (TOne)_containedObject; /// /// The contained item as a TTwo /// public TTwo AsTwo => (TTwo)_containedObject; /// /// Creates a new MultiType that exposes the given item as two different classes /// /// private MultiType(object containedObject) { if (containedObject is TOne && containedObject is TTwo) _containedObject = containedObject; else throw new Exception("The given object must be both a TOne and a TTwo"); } /// /// Creates a new MultiType that exposes the given thing as both a TOne and a TTwo /// /// /// /// public static MultiType Create(T thing) where T : TOne, TTwo => new MultiType(thing); }
用法:
void foo(MultiType thing) { thing.AsOne... // Your code dealing with the thing as an IA thing.AsTwo... // Your code dealing with the thing as an IB }
呼叫者:
foo(MultiType.Create(obj))
请注意,这可以是“链接”: MultiType
可以让您将事物作为字典处理,整数列表, plain enumerable,INotifyPropertyChanged和INotifyCollectionChanged,一次完成。
但同样,这是一个非常糟糕的代码味道 – 可能需要以这种方式处理的类正在做太多。
您将必须定义从两个接口inheritance的第三种类型(可能是接口)。 如果你有这样的限制,那么你肯定应该有一个。 否则它无法使用。 如果这个(obj is IB && obj is IB)
那么obj就是那种类型。
如果没有适用于您可能想要接受的所有对象的类型或界面,我不相信您想要的是什么。 如果您可以控制要处理的事物类型,则应定义一个“inheritance”所有约束的接口,然后让该函数接受该类型的参数。 如果您还想在约束中包含基类型,请定义接口:
接口ISelf(Of T) function自我为T. 结束界面
然后让复合约束接口inheritanceISelf(Of DesiredBaseType)。 例如,如果您要使用一个例程接受从Customer类型派生的对象并实现IDisposable和IEnumerable(Of String)[愚蠢的任意示例],请定义:
接口IDisposableEnumerableOfStringAndSelf(Of T) inheritanceIDisposable,IEnumerable(Of String),ISelf(Of T) 结束界面
然后让这些例程接受IDIsposableEnumerableOfStringAndSelf(Of Customer)。
请注意,这只有在传入的类显式实现IDIsposableEnumerableOfStringAndSelf(Of Customer)或IDIsposableEnumerableOfStringAndSelf(Of T)的某些T(它是Customer的子类型)时才有效。 接口不是duck-typed(事实很重要,因为有一个没有成员的接口是可能的,有时很有用,例如,表明一个类承诺是不可变的)。