实现 .Net 接口(C#)

3

这可能是一个简单的问题。

我有一个接口(MyInterface),它定义了一个属性,如下所示:

IList<IMenuItem> MenuCollection { get; }

实现MyInterface接口的类

public class MyClass : MyInterface
{
    public ObservableCollection<MenuItemBase> MenuCollection 
    {
       get
       {
           ...
       }
    }
    ....
}

根据我所阅读的,ObservableCollection继承自Collection,而Collection是IList类型的。我有一个实现了IMenuItem接口的MenuItemBase类,这样是否就满足了接口要求呢?

我猜接口必须被显式地实现?

我也尝试过这个方法:

public class MyClass : MyInterface
{
    public IList<IMenuItem> MenuCollection MenuCollection 
    {
       get
       {
           if(_menuCollection == null)
              _menuCollection = new ObservableCollection<MenuItemBase>();
           return _menuCollection as IList<IMenuItem>;
       }
    }
    private ObservableCollection<MenuItemBase> _menuCollection;
}

看起来这是一个解决方法的hack(我遇到了一些问题,说MenuCollection没有实例化),以满足接口...是否有更好的方法来实现IInterface1<IInterface2>对象?

我需要这种抽象的原因是因为我正在构建一个prism / unity应用程序,并希望尽可能地将菜单视图模型与显示菜单的功能区UI分离。


3
对我来说,你的“权宜之计”感觉就像是正确的做法... - Stormenet
2
你是否需要将 _menuCollection 强制转换为 IList<IMenuItem>?如果 ObservableCollection<MenuItemBase> 满足 IList<IMenuItem>,那么你应该可以直接返回它... 不是吗? - Dan J
1
此外,我同意@Stormenet的观点,这并不是真正的“黑客”行为。您已经声明了一个方法具有通用返回类型,因此其实现需要具有通用返回类型。这意味着调用该方法的人不需要担心方法实现使用的具体内部类型。在我看来很好。 - Dan J
顺便提一下,你的 as 语句将始终返回 null... - digEmAll
@djacobson - 我尝试直接返回它,但会导致编译时错误,我唯一的猜测是它不喜欢将ObservableCollection作为IList返回 - 当我回到工作时,我需要再次检查确切的错误。 - Chris Klepeis
3个回答

7

Jon是正确的;这与泛型变异无关。然而,他没有提到您想要的功能被称为返回类型协变

也就是说,如果Animal是Giraffe的基本类型,那么返回Animal的接口方法或虚拟方法可以由返回Giraffe的方法实现/专门化。因为每只Giraffe都是Animal,所以合同得到了履行。

C#不支持返回类型协变;事实上,CLR也不支持。一些语言支持返回类型协变;例如,C++就是一个例子。(C++/CLI实现对CLR的限制进行了一些巧妙的技巧。)在C#中,实现的接口方法的返回类型、属性的类型等等,都必须完全匹配。


你提到的 C++/CLI 技巧可能会很有趣 ;) - Jon
@Jon:它们并不那么难以捉摸。只是你所期望的。一堆帮助方法被生成,这些方法只是真正重载的代理。 - Eric Lippert

6

与泛型变量无关

有多个答案谈论了泛型变量的问题,但这与示例无关。如果您尝试使用ArrayList(没有泛型)实现定义为IList的属性,则会发现仍然无法实现。

正确答案

这种行为是因为如果您可以将MenuCollection实现为某些类型的属性,该类型实现了IList<T>(或从中派生,如果它没有被指定为接口),那么这将是可能的:

public interface MyInterface
{
    IList<IMenuItem> MenuCollection { get; set }
}

public class MyClass : MyInterface
{
    // WARNING: Does not count as implementing the interface -- with good reason
    public ObservableCollection<MenuItemBase> MenuCollection { get; set; }
}

var myClass = new MyClass();
var classAsInterface = (MyInterface) myClass; // This is OK of course

classAsInterface.MenuCollection = new List<MenuItemBase>(); // OOPS!!

在最后一行中,你已经分配了一个List<MenuItemBase>(在MyInterface的范围内是可以的,因为MyInterface.MenuCollection的类型是IList<MenuItemBase>),但是你将其分配给了一个类型为ObservableCollection<MenuItemBase>的属性。
当然,这样做是不合法的,因为显然List<MenuItemBase>并不是从ObservableCollection<MenuItemBase>派生出来的。但如果你能这样实现接口,那么这将是一种可能性。

他在接口的MenuCollection属性中只定义了一个getter,没有定义setter。 - Stormenet
4
显然,但语言规则不会因为你定义或未定义 setter 而改变。 - Jon

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接