我是否可以实现一个接口,该接口包含一个子类型属性的接口?

问题描述 投票:9回答:2

我收到以下错误:

ClassName.PropertyName无法实现IClassType.PropertyName,因为它没有匹配的返回类型IBasePropertyType

现在,代码:

public class ClassName : IClassType
{
    public IChildPropertyType PropertyName { get; set; }
}

public interface IClassType
{
    public IBasePropertyType PropertyName { get; set; }
}

public interface IBasePropertyType
{
    // some methods
}

public interface IChildPropertyType : IBasePropertyType
{
    // some methods
}

有没有办法做我正在尝试的事情?我知道问题在于共同/逆转,但我似乎无法弄清楚如何做到这一点。

c# interface covariance contravariance
2个回答
8
投票

要实现给定的接口,您必须具有相同的返回类型。但是,有一些潜在的解决方法可以让生活更轻松:

  1. 使您的界面通用
  2. 明确地实现接口。

如果你使IClassType通用,如下:

public interface IClassType<T> where T : IBasePropertyType
{
    public T PropertyName { get; set; }
}

...然后您可以使用各种属性类型实现此接口:

public class ClassName : IClassType<IChildPropertyType>
{
    public IChildPropertyType PropertyName { get; set; }
}

另一个选择是让你的接口不是通用的,但是要有一个显式实现接口的通用基类型:

public class ClassBase<T> : IClassType
    where T : IChildPropertyType
{
    IBasePropertyType IClassType.PropertyName { 
        get {return PropertyName;}
        set {PropertyName = (IChildPropertyType)value;}
    }
    T PropertyName {get;set;}
}

请注意,最后一个选项并不是很理想,因为您必须动态地将属性转换为给定的子类型:虽然您可以保证每个IChildProperty类型都是IBasePropertyType,但您不能保证每个IBasePropertyType都是IChildPropertyType。但是,如果您可以从原始界面中删除setter,或者您可以采取其他步骤来保证在代码中永远不会使用错误的类型调用setter,那么这可能会起作用。


5
投票

你是正确的,这与协方差有关;特别是它与虚方法返回类型协方差有关,这不是C#语言支持的一种协方差。

请注意,即使它确实如此,您描述的系统也不是类型安全的。假设我们有:

interface IAnimal {}
interface IGiraffe : IAnimal {}
interface ITiger: IAnimal {}
class Tiger : ITiger {}
interface IHaveAnAnimal { IAnimal Animal { get; set; } }
class C : IHaveAnAnimal
{
    public IGiraffe Animal { get; set; }
}
...
IHaveAnAnimal x = new C();
x.Animal = new Tiger(); // Uh oh. We just put a Tiger into a property of type IGiraffe.

即使协方差是合法的,这种协方差也不合法;你必须没有让协方差合法的合法者。

假设你确实没有setter:

interface IAnimal {}
interface IGiraffe : IAnimal {}
interface ITiger: IAnimal {}
class Tiger : ITiger {}
interface IHaveAnAnimal { IAnimal Animal { get; } }
class C : IHaveAnAnimal
{
    public IGiraffe Animal { get; }
}

不幸的是,这仍然不合法。但你可以这样做:

class C : IHaveAnAnimal
{
    IAnimal IHaveAnAnimal.Animal { get { return this.Animal; } }
    public IGiraffe Animal { get; }
}

现在当C用作C时,Animal返回一个长颈鹿,当使用IHaveAnAnimal时,它返回一个IAnimal。

© www.soinside.com 2019 - 2024. All rights reserved.