为什么以下是合法的C#:
public interface ISomeInterface
{
int SomeProperty
{
get;
}
}
public class SomeClassImplementingInterface : ISomeInterface
{
public int SomeProperty
{
get { return 32; }
protected set {}
}
}
但这不是:
public abstract class SomeAbstractClass
{
public abstract int SomeProperty
{
get;
}
}
public class SomeClassExtendingAbstractClass : SomeAbstractClass
{
public override int SomeProperty
{
get { return 32; }
protected set {}
}
}
后者导致以下编译时错误:
'InterfaceAbstractTest.SomeClassExtendingAbstractClass.SomeProperty.set': 无法覆盖因为 'InterfaceAbstractTest.SomeAbstractClass.SomeProperty'没有 一个可覆盖的set访问器InterfaceAbstractTest
在允许前者的同时不解除后者的原因是什么?
答案 0 :(得分:3)
因为使用接口的调用者只关心接口的实现者至少实现接口的定义,如@davisoa所述,而你的示例中的SomeAbstractClass
定义了一个公共合同,准确说明成员的类型,可访问性和(对于属性)可读性/可写性。
如果使用反射来获取SomeProperty
的PropertyInfo(来自基类或子类),则需要从某处解析该信息。允许子类更改可读性/可写性将与返回类型或参数列表中的更改一样违反合同。
想象一下:
SomeAbstractClass sc = new SomeClassExtendingAbstractClass();
PropertyInfo pi = sc.GetType().GetProperty("SomeProperty");
Console.Out.WriteLine(pi.CanWrite); // What should be printed here?
答案 1 :(得分:2)
这是因为接口实现承诺会有一个属性SomeProperty
,你可以"获取"。
抽象类实现承诺它的子类将使用公共get方法提供属性SomeProperty
的实现。
最后,基类定义了一些必须重写的东西,而接口则定义了一个契约。
答案 2 :(得分:1)
您正在尝试覆盖不存在的集合运算符。在抽象类中定义属性的set部分,或者不尝试在具体类中定义一个属性。由于您在具体类中将set设置为protected,因此我想您要做的是在抽象定义中创建受保护的set运算符。
答案 3 :(得分:1)
这是设计的。我引用了C#语言规范:
重写属性声明必须指定完全相同 辅助功能修饰符,类型和名称作为继承属性, if 继承的属性只有一个访问者(即,...只有就绪 或只写),覆盖属性必须仅包含该属性 存取器。强>
这种退出背后的原因可能是因为接口是比抽象类更灵活的合同类型。接口只关心最小公分母而不是整个实现。我认为有充分的理由选择一种设计而不是另一种。
答案 4 :(得分:0)
有必要覆盖现有属性并使用新的读写方法对其进行遮蔽。不幸的是,.net没有提供任何覆盖和隐藏单个类中成员的方法。最好的方法可能是让抽象基类定义一个具体的非虚拟只读属性,其getter调用一个抽象函数。然后,派生类可以使用非虚拟读写函数来遮蔽属性,该函数在其getter中调用相同的函数,并在其setter中调用新的抽象或虚函数。