我想调用一个限制输入类型T的泛型方法来实现两个接口:
interface IA { }
interface IB { }
void foo<T>(T t) where T : IA, IB { }
如何修复
的最后一行void bar(object obj)
{
if (obj is IA && obj is IB)
{
foo((IA && IB)obj);
}
}
反射可能允许进行通话,但我希望保持语言。
答案 0 :(得分:6)
您似乎误解了泛型如何工作:当调用具有泛型参数T
的方法时,T
必须在编译时静态知道。虽然编译器有时可以推断它(因此您并不总是需要明确地将其写下来),但在调用方法时必须提供一些T
。在你的情况下,你所知道的是obj
是IA
和IB
,但这并没有给你足够的信息来呼叫foo<T>
,因为你没有想法T
应该是什么。您必须使用反射,强制转换为实现IA
和IB
的特定类型,或者进行更具戏剧性的设计更改。
答案 1 :(得分:4)
我同意其他响应者的说法,如果你需要这样做,你可能会遇到设计问题,但是你可以用一个实现两个接口的代理对象完成它,并将调用委托给未知对象的两个已经实现的接口实例。现在,当您调用此方法时,您可以为任何支持这两种接口的类型构建代理。
答案 2 :(得分:3)
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,那会破坏吗?我不知道。
答案 3 :(得分:2)
您的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));
}
}
答案 4 :(得分:2)
我不是在宽恕这种做法,但这里有两个选项,其他人没有提到:
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
和{{1接口:
IB
用法:
/// <summary>
/// An ugly hack for when you don't want to create a new wrapper class that inherits from and implements two other interfaces
/// </summary>
/// <typeparam name="TOne"></typeparam>
/// <typeparam name="TTwo"></typeparam>
public sealed class MultiType<TOne, TTwo>
{
/// <summary>
/// The contained item
/// </summary>
private readonly object _containedObject;
/// <summary>
/// The contained item as a TOne
/// </summary>
public TOne AsOne => (TOne)_containedObject;
/// <summary>
/// The contained item as a TTwo
/// </summary>
public TTwo AsTwo => (TTwo)_containedObject;
/// <summary>
/// Creates a new MultiType that exposes the given item as two different classes
/// </summary>
/// <param name="containedObject"></param>
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");
}
/// <summary>
/// Creates a new MultiType that exposes the given thing as both a TOne and a TTwo
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="thing"></param>
/// <returns></returns>
public static MultiType<TOne, TTwo> Create<T>(T thing)
where T : TOne, TTwo
=> new MultiType<TOne, TTwo>(thing);
}
呼叫者:
void foo(MultiType<IA, IB> 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<IA, IB>.Create(obj))
的实例可以让你同时处理作为字典,整数列表,普通可枚举,INotifyPropertyChanged和INotifyCollectionChanged的事物。
但同样,这是一个非常糟糕的代码味道 - 可能需要以这种方式处理的类太多了。
答案 5 :(得分:0)
您必须定义从两个接口继承的第三种类型(可能是接口)。如果你有这样的限制,那么你肯定应该有一个。否则它无法使用。如果这个(obj is IB && obj is IB)
那么obj就是那种类型。
答案 6 :(得分:0)
如果没有适用于您可能想要接受的所有对象的类型或界面,我不相信您想要的是什么。如果您可以控制要处理的事物类型,则应定义一个“继承”所有约束的接口,然后让该函数接受该类型的参数。如果您还想在约束中包含基类型,请定义一个接口:
Interface ISelf(Of Out T) Function Self As T End Interface然后让复合约束接口继承ISelf(Of DesiredBaseType)。例如,如果您要使用一个例程接受从Customer类型派生的对象并实现IDisposable和IEnumerable(Of String)[愚蠢的任意示例],请定义:
Interface IDisposableEnumerableOfStringAndSelf(Of T) Inherits IDisposable, IEnumerable(Of String), ISelf(Of T) End Interface
然后让这些例程接受IDIsposableEnumerableOfStringAndSelf(Of Customer)。
请注意,这只有在传入的类显式实现IDIsposableEnumerableOfStringAndSelf(Of Customer)或IDIsposableEnumerableOfStringAndSelf(Of T)的某些T(它是Customer的子类型)时才有效。接口不是duck-typed(这一事实很重要,因为有一个没有成员的接口是可能的,有时很有用,例如,表明一个类承诺是不可变的。)