2009-09-30 57 views
2

我有一个像vb.net C#属性overiding机制

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 

在vb.net中的一类,并从它

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 

一个类派生这给了我下面的错误 公共重写只写属性名称()作为字符串'不能覆盖'公共Overrideable属性名称()作为字符串',因为他们不同'只读'或'只写'

但我有相同的构造在C#

public class Customer 
{ 
    public virtual string FirstName { get; set; }  
} 

公共类CustomerProxy:因为两种语言都非常不一致的行为方式的客户 {

public override string FirstName 
    { 
     set 
     { 
      base.FirstName = value; 
     } 
    } 

}

它的工作原理,所以第一件事情就是,这是一致的。

其次,当我做了反射来获取属性,因此,例如

Dim propInfo = GetType(Customer).GetProperty("Name") 

的propINfo.canRead属性始终是假的,不应该因为这个基类实现属性的getter是真实的?

非常感谢

回答

2

我会先处理第二部分。在您当前的vb.net代码中,派生的Name属性替换原始的反射查找。因此,反射代码只能看到WriteOnly版本的属性。更糟糕的是,你完全替换了派生setter中的后台存储,所以你的getter正在检查一个完全不同于你设置的变量。

关于第一个问题,压倒一切的在vb.net属性时,你总是需要同时覆盖getter和二传手,即使你只是用相同的实现来替代它:

代码应该读起来像这个:

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 
+0

但是不应该反映准确反映你是否可以实际从财产中读取或没有? – 2009-09-30 14:58:44

+0

所以基本上你说反射可以说“不,你不能从属性X读取”,实际上你可以?如果您使用反射从属性中读取,会发生什么情况,会不会也会失败?这似乎与我不一致。我明白,反射只是看一些元数据表,但如果可以读取属性,我只是觉得反射应该准确地报告。 – 2009-09-30 15:01:22

+0

不,因为反射查询只检查新的继承属性。它不知道父母的任何事情。这种情况也有助于说明为什么_right_这样做,因为你无法从属性中读取。你可以看到这个,如果你第一次在属性上设置一个新的值。如果你从中读出,你会发现你并不真正从那个属性读取,而是从其他地方读取,因为它没有显示你的改变。 – 2009-09-30 15:02:04

0

你发布的两个(c#和vb)派生类是不相同的。为了尽量获得相同的行为(不会编译):

public override string FirstName 
    { 
     protected get { return base.FirstName}; 
     set 
     { 
      base.FirstName = value; 
     } 
    } 

你会得到“覆盖访问....无法改变的访问权限。”实际发生的事情是你只覆盖set访问器。

0

这里是excerpt from 10.6.3 Virtual, sealed, override, and abstract accessors

重写属性声明必须以继承财产指定完全相同的可访问性修饰符,类型和名称。如果继承的属性只有一个访问者(即,如果继承的属性是只读或只写的),则重写属性必须只包含该访问者。如果继承的属性包括两个访问器(即,如果继承的属性是读写),则覆盖属性可以包括单个访问器或两个访问器。

因此C#按预期行事。

+0

我并不感到意外,只是重写一个setter,尽管通常不会这样做。然而,我感到惊讶的是,反射不允许或报告正常的C#代码实际上可以做什么。 – 2009-09-30 15:11:38

1

我对此有点惊讶,因为它似乎是由Joel回答,这实际上是它应该如何工作。然而在我看来,它似乎是这样的:“即使你在后代类中取代它,只实现了setter,但属性X仍然可以被读取,但是反射说不,你不能读取它”。

这对我来说看起来不一致。 “是的,你可以使用普通的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 

注意,甚至通过在基地Value属性写入时(当我有一个派生的实例),它仍然通过覆盖的属性去。

和代码:

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; 
      } 
     } 
    } 
} 
+0

关键是派生类型是一种全新的类型。所以反射调用和派生只返回派生属性的数据。由于名称不明确,可能存在一个错误,但派生属性是一个全新的属性,您需要某种方式来查询仅属于该属性的反射。 – 2009-09-30 15:22:49

+0

但是它在使用派生实例时覆盖了base的setter,所以它不仅仅是一个新的属性。看到我更新的答案,注意当使用“Base b = new Derived()”并通过Value进行设置时,它将通过重写的代码。 – 2009-09-30 15:46:16

+0

当我反映一些C++ \ CLI代码时,我也注意到了它。所以不一致也发生在C++ \ CLI中。 – 2010-12-10 22:37:28

0

在C#,单个属性声明可以生成只读,只写,和/或根据需要以满足任何虚拟属性重写或接口实现读写性能。如果例如需要一个属性来实现只读和读写接口,编译器将生成一个只读属性,该属性忽略代码中包含的任何set以及同时使用getset的读写属性。如果该属性重写读写属性,但未指定getset,则未指定的操作将链接到父级中定义的操作。这种行为不同于VB.NET,其中每个属性声明都将创建与其指示的属性完全相同的属性。