Свойства только для записи являются редким зверем и не существуют в пространстве 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();
}
}
Ваши комментарии; Я опубликую обновление с «трюком» для сценария, в котором вы не знаете типы ... –
На самом деле, я просто перечитываю ваши комментарии; является ли свойство записи только для интерфейса? Если это так, вы можете использовать исходный код (см. Правки) и просто использовать интерфейс в качестве отраженного типа. –
Да, свойство находится на интерфейсе. Я попробовал это с вашим оригинальным примером (до редактирования, если это то, что вы имеете в виду?), Но я получил исключение из-за невозможности связать делегата - или что-то в этом роде - мне нужно попробовать еще раз, чтобы получить фактическое сообщение об исключении , – asgerhallas