我在vb.net中有一个类
Public Class Customer
Private _Name As String
Public Overridable Property Name() As String
Get
Return _Name
End Get
Set(ByVal value As String)
_Name = value
End Set
End Property
End Class
和从中派生的类
Public Class ProxyCustomer
Inherits Customer
Private _name As String
Public Overrides WriteOnly Property Name() As String
Set(ByVal value As String)
_name = value
End Set
End Property
End Class
这给了我以下错误 Public Overrides WriteOnly Property Name()As String'不能覆盖'Public Overridable Property Name()As String'因为它们的区别是'ReadOnly'或'WriteOnly'
但我在C#中有相同的构造
public class Customer
{
public virtual string FirstName { get; set; }
}
公共类CustomerProxy:Customer {
public override string FirstName
{
set
{
base.FirstName = value;
}
}
}
哪个有效,所以首先,这是一致的,因为2种语言的行为方式非常不一致。
其次,当我做一个反射来获得一个属性时,例如
Dim propInfo = GetType(Customer).GetProperty("Name")
propINfo.canRead属性总是false,因为基类实现了属性的getter,所以不应该这样吗?
非常感谢答案 0 :(得分:2)
我先讨论第二部分。在当前的vb.net代码中,派生的Name属性替换了反射查找的原始属性。因此,反射代码只能看到属性的WriteOnly
版本。更糟糕的是,您完全替换派生的setter中的后备存储,因此您的getter正在检查与您设置的变量完全不同的变量。
至于第一个问题,当重写vb.net中的属性时,你总是需要覆盖getter和setter,即使你只是用相同的实现替换它:
代码应如下所示:
Public Class ProxyCustomer
Inherits Customer
Public Overrides Property Name() As String
Get
Return MyBase.Name ''# Return the original parent property value
End Get
Set(ByVal value As String)
MyBase.Name = value
End Set
End Property
End Class
答案 1 :(得分:1)
我对此感到有些惊讶,因为Joel的答案似乎确实是这应该是如何运作的。
但是,在我看来,它看起来像这样:“属性X可以读取,即使你在后代类中覆盖它并且只实现了setter,但反射说不,你不能读它”。 / p>
对我来说,这看起来不一致。 “是的,您可以使用普通的C#代码从该属性中读取,但不会,反射不会说您可以也不会允许您从中读取”。
这是一段代码示例。代码报告:
Base.Value.CanRead: True
Base.Value.CanWrite: True
Derived.Value.CanRead: False
Derived.Value.CanWrite: True
Setting via Derived.Value
Derived.Value: Test
(reflection get crashes with method Get not found, so commented it out)
Setting via Derived.Value
Base.Value: Test2
请注意,即使在Base上写入Value属性(当我有Derived实例时),它仍然会通过重写的属性。
和代码:
using System;
using System.Reflection;
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
Type type = typeof(Base);
PropertyInfo prop = type.GetProperty("Value");
Console.Out.WriteLine("Base.Value.CanRead: " + prop.CanRead);
Console.Out.WriteLine("Base.Value.CanWrite: " + prop.CanWrite);
type = typeof(Derived);
prop = type.GetProperty("Value");
Console.Out.WriteLine("Derived.Value.CanRead: " + prop.CanRead);
Console.Out.WriteLine("Derived.Value.CanWrite: " + prop.CanWrite);
Derived d = new Derived();
d.Value = "Test";
Console.Out.WriteLine("Derived.Value: " + d.Value);
// Console.Out.WriteLine("Reflected value: " + prop.GetValue(d, null));
Base b = new Derived();
b.Value = "Test2";
Console.In.ReadLine();
}
}
public class Base
{
public virtual String Value { get; set; }
}
public class Derived : Base
{
public override string Value
{
set
{
Console.Out.WriteLine("Setting via Derived.Value");
base.Value = value;
}
}
}
}
答案 2 :(得分:0)
您发布的两个(c#和vb)派生类不等效。要尝试获得相同的行为(不会编译):
public override string FirstName
{
protected get { return base.FirstName};
set
{
base.FirstName = value;
}
}
您将获得“覆盖访问者....无法更改访问权限”。实际发生的是你只覆盖了set accessor。
答案 3 :(得分:0)
以下是excerpt from 10.6.3 Virtual, sealed, override, and abstract accessors:
重写属性声明必须指定与继承属性完全相同的辅助功能修饰符,类型和名称。如果继承的属性只有一个访问者(即,如果继承的属性是只读的或只写的),则覆盖属性必须只包含该访问者。如果继承的属性包含两个访问器(即,如果继承的属性是读写),则覆盖属性可以包括单个访问器或两个访问器。
所以C#按预期行事。
答案 4 :(得分:0)
在C#中,单个属性声明可以根据需要生成只读,只写和/或读写属性,以满足任何虚拟属性覆盖或接口实现。如果是需要一个属性来实现只读和读写接口,编译器将生成一个只读属性,忽略代码中包含的任何set
,以及一个同时使用{{{}的读写属性1}}和get
。如果属性覆盖了读写属性,但未指定set
或get
,则未指定的操作将链接到父级中定义的操作。这种行为与VB.NET不同,其中每个属性声明都将创建由此指定的属性类型。