2009-09-30 52 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,其中每個屬性聲明都將創建與其指示的屬性完全相同的屬性。