【问题标题】:Is there a way to declare a variable that implements multiple interfaces in .Net?有没有办法声明一个在.Net中实现多个接口的变量?
【发布时间】:2011-02-14 21:42:23
【问题描述】:

类似于this Java question。我想指定一个变量实现多个接口。比如

private {IFirstInterface, ISecondInterface} _foo;

public void SetFoo({IFirstInterface, ISecondInterface} value)
{
    _foo = value;
}

要求:

  • 我无法将接口添加到将传递给 Foo 的大多数类型。所以我无法创建继承自 IFirstInterface 和 ISecondInterface 的第三个接口。
  • 如果可能,我希望避免将包含的类设为泛型,因为 Foo 的类型与类没有太大关系,而且用户在编译时不太可能知道它。
  • 稍后我需要使用 foo 来访问这两个接口中的方法。
  • 我想以编译器安全的方式执行此操作,即在尝试使用接口之前不要尝试强制转换为接口。如果 foo 没有实现这两个接口,那么相当多的功能将无法正常工作。

这可能吗?

编辑:出于各种原因,我多次想要这个。在这种情况下,这是因为我将一组属性从 ObservableCollections 更改为 ReadOnlyObservableCollections。我有一个帮助类,它创建从源 ObservableCollection 到另一个集合的投影。由于 ReadOnlyObservableCollection 不继承自 ObservableCollection 并且我只需要 IList 和 INotifyCollectionChanged 中的操作,我希望将我的源集合存储为这两个接口的组合,而不是需要 ObservableCollection。

【问题讨论】:

  • 简短回答:不。并非没有定义从 IFirstInterface 和 ISecondInterface 继承的第三个接口。
  • 我唯一的想法是你可以有相同的实例作为两个接口变量?你在什么情况下需要这个?您能否提供一些关于您在哪里需要这样的东西的见解?
  • 所以我不反对创建一个简单的类来处理这个问题。
  • @drachenstern 类已经存在。假设这两个接口是 IList 和 INotifyCollectionChanged。用户可以提供 ObservableCollection、ReadOnlyObservableCollection、CompositeCollection、FreezableCollection 或 ObservableDictionary。除了 ObservableDictionary 之外,这些都内置在 .Net 中。我也有很多我想要的实例,其中一个接口是 ISerializable。
  • @Bryan Anderson - 是的,但如果它是一个适当的类,那么它会同时获得它们,你不必做时髦的语义或演员,对吧?我想如果它使代码更简单,它会起作用,但我只是认为它有点难以阅读。 YMMV。

标签: .net interface


【解决方案1】:

如果你想限制你的方法SetFoo 只接受实现这两个接口的参数,那么你是运气

public void SetFoo<T>(T value) where T : IFirstInterface, ISecondInterface
{
    _foo = value;
}

从现在开始,任何时候您想将_foo 成员作为这两个接口之一访问,您只需分别通过强制转换访问它:(IFirstInterface)_foo(ISecondInterface)_foo

您确实说过您希望避免使用强制转换来确保编译时安全;但我认为,如果你想办法确保_foo 使用上面的SetFoo 进行初始化,你就可以高枕无忧了。


我刚刚意识到以下内容描述了您在您不想想要做的问题中具体陈述的内容。所以,我说,去吧。

另一个想法是,因为它看起来像这个 _foo 对象是一个类成员(我基于变量名中的 _ 这个假设),你可以这样定义你的类:

class YourClassThatHasAFoo<T> where T : IFirstInterface, ISecondInterface {
    private T _foo;

    public void SetFoo(T value) {
        _foo = value;
    }
}

这是否真的有意义显然取决于您的具体情况。您一定要考虑清楚,因为具有此类约束的类有时会导致您可能无法预料到的问题(例如 SetFoo 现在 必须 采用 @ 类型的参数987654333@,而不仅仅是实现您的两个接口的任何类)。

【讨论】:

【解决方案2】:

不,没有办法以声明方式确保特定实例实现多个接口。

一个选项可能使用泛型,尽管这实际上只适用于函数而不是属性。

public void Foo<T>(T arg)
    where T : IFirstInterface
    where T : ISecondInterface
{
    ...
}

使用类型推断,你应该可以这样称呼它:

ConcreteType bar = new ConcreteType(); // this implements both

Foo(bar);

【讨论】:

    【解决方案3】:

    可以定义接口,以便它们的任意组合可能需要并以类型安全的方式使用。关键是定义一个接口 ISelf(Of Out T),它的一个成员 Self 是一个属性,对象通过该属性将自身返回为一个 T,然后对于每个接口 IFoo,声明一个对应的泛型 IFoo(Of Out T),它继承自 IFoo 和 ISelf(Of T)。实现 ISelf(Of Wowzo)、IFoo(Of Wowzo)、IBar(Of Wowzo) 和 IBoz(Of Wowzo) 的类 Wowzo 将实现 IFoo(Of IBar)、IBar(Of IFoo)、IFoo(Of IBoz(Of IBar))、IFoo(Of IBar(Of IFoo(Of IBar(Of IBoz(Of IBar))))) 等,如有必要,可以强制转换为任何此类类型。如果 Thing 是 IFoo(Of IBar(Of IBoz)),则可以直接将其用作 IFoo,或将 Thing.Self 用作 IBar,或将 Thing.Self.Self 用作 IBoz。

    如何使用 C# 组合三个接口的示例:

    public interface ISelf<out T>
    {
        T Self { get; }
    }
    interface IA { }
    interface IA<T> : IA, ISelf<T> { }
    interface IB { }
    interface IB<T> : IB, ISelf<T> { }
    interface IC { }
    interface IC<T> : IC, ISelf<T> { }
    class ConcreteThing
        : IA<ConcreteThing>
        , IB<ConcreteThing>
        , IC<ConcreteThing>
    {
        public ConcreteThing Self => this;
    }
    public class ReferencingObject
    {
        IA<IB<IC>> Thing { get; }
    
        void Method()
        {
            IA a = Thing;
            IB b = Thing.Self;
            IC c = Thing.Self.Self;
        }
    }
    

    【讨论】:

      【解决方案4】:

      .NET 中的变量具有类型,并且类型可以实现多个接口。像这样的:

      public interface Interface1 { }
      public interface Interface2 { }
      public class SupportsMuliple : Interface1, Interface2 { }
      

      但是从您的问题来看,您似乎希望有一个变量实现多个接口,而没有实现这些接口的类型。这实际上是不可能的,但是您可以动态生成一个实现接口的类型,并隐藏有一个真正的类型在发挥作用的事实。模拟库 moq 使用 Castle DynamicProxy 执行此操作。

      【讨论】:

      • 感谢您的回复。实现接口的类型已经存在,我只需要一种方法来以与实现无关的方式存储和使用它们。
      猜你喜欢
      • 2015-11-05
      • 2014-04-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多