将对象同时转换为两个接口,以调用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参数TT必须在编译时静态知道。 虽然编译器有时可以推断它(因此您并不总是需要明确地将其写下来),但在调用方法时必须提供一些T 在你的情况下,你所知道的是obj是一个IA和一个IB ,但这并没有给你足够的信息来调用foo ,因为你不知道应该是什么。 您必须使用reflection,强制转换为实现IAIB特定类型,或者进行更具戏剧性的设计更改。

我同意其他响应者的说法,如果你需要这样做,你可能会遇到设计问题,但是你可以用一个实现两个接口的代理对象完成它,并将调用委托给未知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); 

它不漂亮,但它可能是一个选择。

如果你喜欢大流行的黑客攻击

如果你发现自己需要做很多事情,那就是难闻的气味而且有更好的设计。

但是如果你没有选择并且这是“需要”到处都是,那么这可能会让你的生活更轻松,因为你不必浪费时间创建实现你的IAIB接口的类:

 ///  /// 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, IList>, 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(事实很重要,因为有一个没有成员的接口是可能的,有时很有用,例如,表明一个类承诺是不可变的)。