Странный эффект с переопределенными свойствами и отражением - PullRequest
13 голосов
/ 15 ноября 2011

Я столкнулся со странным поведением в .NET / Reflection и не могу найти никакого решения / объяснения этому:

class A 
{
   public virtual string TestString { get; set; }
}

class B : A
{
   public override string TestString
   {
      get { return "x"; }
   }
}

Поскольку свойства - это просто пары функций (get_PropName(), set_PropName()), переопределяющие только часть "get", она должна оставить часть "set", как и в базовом классе. И это именно то, что происходит, если вы пытаетесь создать экземпляр класса B и присвоить значение TestString, он использует реализацию класса A.

Но что произойдет, если я посмотрю на экземпляр объекта класса B в отражении, это:

PropertyInfo propInfo = b.GetType().GetProperty("TestString");
propInfo.CanRead  ---> true
propInfo.CanWrite ---> false(!)

И если я попытаюсь вызвать сеттер из отражения с помощью:

propInfo.SetValue("test", b, null);

Я даже получу ArgumentException со следующим сообщением:

Метод набора свойств не найден.

Это как и ожидалось? Потому что мне кажется, что я не могу найти комбинацию BindingFlags для метода GetProperty(), которая возвращает мне свойство с работающей парой get / set из отражения.

EDIT: Я ожидал бы такого поведения, если бы использовал BindingFlags.DeclaredOnly на GetProperties(), но значение по умолчанию (BindingFlags.Default) учитывает унаследованные элементы, и установщик TestString явно наследуется!

Ответы [ 2 ]

6 голосов
/ 15 ноября 2011

Вот обходной путь:

typeof(B).GetProperty("TestString")
         .GetAccessors()            // { B.get_TestString() }
         .First()                   // B.get_TestString()
         .GetBaseDefinition()       // A.get_TestString()
         .DeclaringType             // typeof(A)
         .GetProperty("TestString") // A.TestString: CanRead and CanWrite

Этот подход должен быть достаточно надежным.Вам нужно быть более осторожным с этим (BindingFlags), если вы ищете непубличные средства доступа.

EDIT:

Обратите внимание, что этот подход отличается от "жесткого кодирования" typeof(A).GetProperty("TestString") или typeof(B).BaseType.GetProperty("TestString"), поскольку он находит фактический тип original , который объявляет свойство ввопрос.Поскольку невозможно (по крайней мере, в C #) для производного типа добавить новые средства доступа к переопределенному свойству, объявление свойства в этом "оригинальном" типе должно содержать все соответствующие средства доступа.

3 голосов
/ 15 ноября 2011

Вы не перезаписываете метод, вы перезаписываете определение свойства

Определение свойства по умолчанию включает в себя Get / Set методы, а ваше новое определение включает только Getметод, поэтому имеет смысл, что у вашего перезаписанного свойства есть только Get, а не Set

Редактировать

Если вы запустите что-то вроде Reflector на этомВы увидите, что

class A 
{
   public virtual string TestString { get; set; }
}

class B : A
{
   public override string TestString
   {
      get { return "x"; }
   }
}

компилируется во что-то похожее на это

internal class A
{
    // Fields
    [CompilerGenerated]
    private string <TestString>k__BackingField;

    // Methods
    public A();

    // Properties
    public virtual string TestString { [CompilerGenerated] get; [CompilerGenerated] set; }
}

internal class B : A
{
    // Methods
    public B();

    // Properties
    public override string TestString { get; }
}

Когда вы устанавливаете значение в коде, вы на самом деле вызываете что-то вроде B.base.set_TestValue.Когда вы что-то отражаете, вы пытаетесь найти B.set_TestValue, которого не существует.

Хотя верно, что вы не можете перезаписать свойство, вы можете переписать определение свойства (если оно не конфликтует сопределение базового свойства).Поскольку ваш вопрос изначально был помечен WPF, я думал о DependencyProperties в то время, которые на самом деле являются определениями свойств, а не свойствами в том смысле, о котором вы, возможно, думаете.

...