2009-04-21 2 views
1

Я пытаюсь получить все свойства от типа, но с помощью TypeDescriptor.GetProperties (thisType) будет поставлять только свойства, которые имеют как setter, так и геттер. У меня есть свойства только для записи. Есть ли способ получить PropertyDescriptorCollection, в том числе?TypeDescriptor.GetProperties (thisType), чтобы возвращать свойства, которые только для записи

/Asger

+0

Ваши комментарии; Я опубликую обновление с «трюком» для сценария, в котором вы не знаете типы ... –

+0

На самом деле, я просто перечитываю ваши комментарии; является ли свойство записи только для интерфейса? Если это так, вы можете использовать исходный код (см. Правки) и просто использовать интерфейс в качестве отраженного типа. –

+0

Да, свойство находится на интерфейсе. Я попробовал это с вашим оригинальным примером (до редактирования, если это то, что вы имеете в виду?), Но я получил исключение из-за невозможности связать делегата - или что-то в этом роде - мне нужно попробовать еще раз, чтобы получить фактическое сообщение об исключении , – asgerhallas

ответ

9

Свойства только для записи являются редким зверем и не существуют в пространстве System.ComponentModel/PropertyDescriptor. PropertyDescriptor s предназначены для чтения. Возможно, я мог бы взломать HyperDescriptor, чтобы обмануть свойства только для записи, но это был бы взлом - и, вероятно, ему придется бросать исключения для get, что может сильно повлиять на код вызова.

В стороне; Обычно я рекомендую против свойств только для записи; в примере с текстовыми книгами, которые люди вытаскивают, это пароли (public string Password {private get;set;}). Я бы предпочел использовать метод void SetPassword(string newPassword) ...

Что вы на самом деле хотите сделать? Есть целый ряд вариантов здесь, все очень достижимо:

  • использование отражения в одиночку (медленно, возможно, не вариант)
  • использование Delegate.CreateDelegate (очень легко)
  • использование Expression.Compileнемного сложнее, но не так много)
  • использование Reflection.Emit (довольно трудно)
  • подкладок только для записи свойства в PropertyDescriptor (довольно жесткие)

Если вы дадите мне знать, что вы на самом деле хотите сделать (вместо того, как вы в настоящее время пытаетесь это сделать), я мог бы помочь больше.

В качестве примера, используя Delegate.CreateDelegate (обратите внимание, что вы хотели спрятать делегат где-то и повторно использовать его много раз):

отредактированные, чтобы показать, как это сделать, если вы не знаете, конкретные типы во время выполнения

using System; 
using System.Reflection; 

class Foo 
{ 
    public string Bar { private get; set; } 
    public override string ToString() 
    { 
     return Bar; // to prove working 
    } 
} 
static class Program 
{ 
    static void Main() 
    { 
     ISetter setter = Setter.Create(typeof(Foo), "Bar"); 
     Foo foo = new Foo(); 
     setter.SetValue(foo, "abc"); 
     string s = foo.ToString(); // prove working 
    } 
} 
public interface ISetter { 
    void SetValue(object target, object value); 
} 
public static class Setter 
{ 
    public static ISetter Create(Type type, string propertyName) 
    { 
     if (type == null) throw new ArgumentNullException("type"); 
     if (propertyName == null) throw new ArgumentNullException("propertyName"); 
     return Create(type.GetProperty(propertyName)); 
    } 
    public static ISetter Create(PropertyInfo property) 
    { 
     if(property == null) throw new ArgumentNullException("property"); 
     if (!property.CanWrite) throw new InvalidOperationException("Property cannot be written"); 
     Type type = typeof(TypedSetter<,>).MakeGenericType(
       property.ReflectedType, property.PropertyType); 
     return (ISetter) Activator.CreateInstance(
      type, property.GetSetMethod()); 
    } 
} 

public class TypedSetter<TTarget, TValue> : ISetter { 
    private readonly Action<TTarget, TValue> setter; 
    public TypedSetter(MethodInfo method) { 
     setter = (Action<TTarget, TValue>)Delegate.CreateDelegate(
      typeof(Action<TTarget, TValue>), method); 
    } 
    void ISetter.SetValue(object target, object value) { 
     setter((TTarget)target, (TValue)value); 
    } 
    public void SetValue(TTarget target, TValue value) { 
     setter(target, value); 
    } 
} 

Или же с помощью Expression API (.NET 3.5):

using System; 
using System.Linq.Expressions; 
using System.Reflection; 

class Foo 
{ 
    public string Bar { private get; set; } 
    public override string ToString() 
    { 
     return Bar; // to prove working 
    } 
} 
static class Program 
{ 
    static void Main() 
    { 
     Action<object,object> setter = Setter.Create(typeof(Foo), "Bar"); 
     Foo foo = new Foo(); 
     setter(foo, "abc"); 
     string s = foo.ToString(); 
    } 
} 

public static class Setter 
{ 
    public static Action<object,object> Create(Type type, string propertyName) 
    { 
     if (type == null) throw new ArgumentNullException("type"); 
     if (propertyName == null) throw new ArgumentNullException("propertyName"); 
     return Create(type.GetProperty(propertyName)); 
    } 
    public static Action<object,object> Create(PropertyInfo property) 
    { 
     if(property == null) throw new ArgumentNullException("property"); 
     if (!property.CanWrite) throw new InvalidOperationException("Property cannot be written"); 

     var objParam = Expression.Parameter(typeof(object), "obj"); 
     var valueParam = Expression.Parameter(typeof(object), "value"); 
     var body = Expression.Call(
      Expression.Convert(objParam, property.ReflectedType), 
      property.GetSetMethod(), 
      Expression.Convert(valueParam, property.PropertyType)); 
     return Expression.Lambda<Action<object, object>>(
      body, objParam, valueParam).Compile(); 
    } 
} 
+0

Привет, Марк, большое спасибо за ваш быстрый ответ! Установки работают как прокси-сервер для настройки некоторых элементов вида в приложении веб-форм. Ведущий через интерфейс просмотра установит требуемую конфигурацию в представлении - он использует отражение над интерфейсом и реализацию представления, чтобы в конечном итоге использовать SetValue для установки конфигурации в представлении. По мере роста наших систем, и существует много представлений (вложенных друг в друга), часть отражения происходит очень медленно. – asgerhallas

+0

Я думаю, что ваш делегат-решение кажется правильным для меня - у меня есть логика для SetValue, спрятанная в общем классе. Я сразу же попробую. Есть ли еще документация/«начатые записи» о любых других возможностях, которые вы порекомендовали бы? – asgerhallas

+0

Я только немного посмотрел на него. Одна из проблем с решением делегата заключается в том, что я не знаю тип реализации представления (ваш Foo) во время компиляции, только интерфейс, который реализует Foo. Во время выполнения у меня есть тип. Затем я должен использовать отражение для создания делегата - я не знаю, разрушит ли это производительность тогда ... – asgerhallas

2

Используйте System.Type.GetProperties() вместо этого, который возвращает все свойства. Обратите внимание, что это возвращает PropertyInfo[] вместо PropertyDescriptorCollection.

+1

FYI: только версия без параметров сохраняет все общедоступные свойства.Есть перегрузка, которая требует некоторых BindingFlags, которые вы можете использовать, чтобы получить все свойства, если хотите. –

+0

Спасибо за помощь. Но мне нужен PropertyDescriptor, а не PropertyInfo, поскольку я использую Marc Gravell HyperTypeDescriptor, потому что проблемы с производительностью. Поэтому использование Type.GetProperties, к сожалению, не является решением. – asgerhallas

+0

Я слышал, как мое имя вызывается? Я добавлю ответ ... –

Смежные вопросы