我可以实现一个包含接口所需子类型属性的接口吗?
我收到以下错误:
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
}
有没有办法做我正在尝试的事情?我知道问题在于协变/逆变,但我似乎不知道如何做到这一点。
I am receiving the following error:
ClassName.PropertyName cannot implement IClassType.PropertyName
because it does not have the matching return type of IBasePropertyType
Now, for the code:
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
}
Is there a way to do what I am attempting? I know that the issue is with co/contravariance, but I can't seem to figure out how to do this.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
为了实现给定的接口,您必须具有相同的返回类型。然而,有一些潜在的解决方法可以让生活变得更轻松:
如果您将
IClassType
设为泛型,如下所示:...那么您可以使用各种属性类型来实现此接口:
另一种选择是让您的接口保持非泛型,但要有一个泛型基类型显式实现接口:
请注意,最后一个选项不太理想,因为您必须动态地将属性转换为给定的子类型:虽然您可以保证每个 IChildProperty 类型都是 IBasePropertyType,但不能保证每个 IBasePropertyType 都是 IChildPropertyType。但是,如果您可以从原始接口中消除 setter,或者可以采取其他步骤来保证代码中永远不会使用错误的类型调用 setter,那么这可能会起作用。
更新
这不会从根本上改变答案,但 C# 现在支持协变和逆变接口。例如,如果您只希望接口公开属性上的 getter,则可以将该类型标记为
out
类型:实现类型不必更改任何内容 - 它们仍然可以实现如果他们愿意,则设置器:
然后,即使您的类实现了
IClassType
,它仍然可以转换为IClassType
并用作针对该接口编写的任何方法的参数:In order to implement the given interface, you must have the same return type. However, there are a couple of potential work-arounds to make life easier:
If you make
IClassType
generic, like so:... then you can implement this interface using various property types:
Another option would be to leave your interface non-generic, but to have a generic base type that explicitly implements the interface:
Note that this last option is not quite ideal because you must dynamically cast the property to the given child type: while you can guarantee that every IChildProperty type is an IBasePropertyType, you cannot guarantee that every IBasePropertyType is an IChildPropertyType. However, if you can eliminate the setter from the original interface, or if you can take other steps to guarantee that the setter will never be called with the wrong type in your code, then this could work.
Update
This doesn't fundamentally change the answer much, but C# now supports covariant and contravariant interfaces. For example, if you only expect your interface to expose the getter on your property, you could mark the type as an
out
type:Implementing types don't have to change anything--they can still implement the setter if they want to:
Then, even though your class implements
IClassType<IChildPropertyType>
, it can still be cast as anIClassType<IBasePropertyType>
and used as an argument to any method written against that interface:你是对的,这与协方差有关;具体来说,它与虚拟方法返回类型协方差有关,这不是 C# 语言支持的一种协方差。
更新:这个答案已经有十多年了。 C# 可能很快就会实现返回类型协方差。请参阅 https://github.com/dotnet/csharplang/issues/49 了解细节。
请注意,即使确实如此,您描述的系统也不是类型安全的。假设我们有:
即使协方差完全合法,这种协方差也不会合法;您必须没有设置器才能使协方差合法。
假设你没有 setter:
不幸的是这仍然不合法。但你可以这样做:
现在,当 C 用作 C 时,Animal 返回长颈鹿,当用作 IHaveAnAnimal 时,它返回 IAnimal。
You are correct that this has to do with covariance; specifically it has to do with virtual method return type covariance, which is not a kind of covariance that the C# language supports.
UPDATE: This answer is over ten years old. C# may soon implement return type covariance. Please see https://github.com/dotnet/csharplang/issues/49 for details.
Note that even if it did, the system you describe is not type safe. Suppose we have:
Even if the covariance were legal at all, this kind of covariance would not be legal; you'd have to have no setter for the covariance to be legal.
Suppose then you did have no setter:
Unfortunately this is still not legal. But you can do this:
Now when C is used as a C, Animal returns a giraffe, and when used an an IHaveAnAnimal, it returns an IAnimal.