将对象同时转换为两个接口,以调用泛型方法

时间:2010-05-24 17:30:54

标签: c# .net generics interface casting

我想调用一个限制输入类型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);
    }
}

反射可能允许进行通话,但我希望保持语言。

7 个答案:

答案 0 :(得分:6)

您似乎误解了泛型如何工作:当调用具有泛型参数T的方法时,T必须在编译时静态知道。虽然编译器有时可以推断它(因此您并不总是需要明确地将其写下来),但在调用方法时必须提供一些T 。在你的情况下,你所知道的是objIAIB,但这并没有给你足够的信息来呼叫foo<T>,因为你没有想法T应该是什么。您必须使用反射,强制转换为实现IAIB特定类型,或者进行更具戏剧性的设计更改。

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