Les propriétés en écriture seule sont rares et n'existent pas dans l'espace System.ComponentModel/PropertyDescriptor. PropertyDescriptor
s sont conçus pour être lisibles. Je pourrais probablement pirater HyperDescriptor
pour caler les propriétés en écriture seule, mais ce serait un hack - et il faudrait vraisemblablement jeter des exceptions pour get
, ce qui pourrait avoir un impact sur le code d'appel.
En aparté; Je conseille généralement contre propriétés en écriture seule; l'exemple de livre de texte que les gens trotter est des mots de passe (public string Password {private get;set;}
) - Je préfère de beaucoup avoir une méthode void SetPassword(string newPassword)
...
Qu'est-ce que vous voulez réellement faire? Il existe une gamme d'options ici, très réalisable:
- utilisation seule réflexion (lent, peut-être pas une option)
- utilisation
Delegate.CreateDelegate
(très facile)
- utilisation
Expression.Compile
(un peu plus difficile, mais pas beaucoup)
- utilisation
Reflection.Emit
(assez difficile)
- propriétés shim en écriture seule dans
PropertyDescriptor
(assez difficile)
Si vous me faites savoir ce que vous voulez réellement faire (plutôt que la façon dont vous essayez actuellement de le faire), je pourrais peut-être vous aider davantage.
À titre d'exemple en utilisant Delegate.CreateDelegate
(note que vous voulez cacher quelque part le délégué et réutilisez-le beaucoup de fois):
édités pour montrer comment le faire si vous ne connaissez pas les types spécifiques lors de l'exécution
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);
}
}
Ou bien en utilisant l'API Expression
(.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();
}
}
Re vos commentaires; Je vais poster une mise à jour avec un "truc" pour le scénario où vous ne connaissez pas les types ... –
En fait, je viens de relire vos commentaires; est la propriété en écriture seule sur l'interface? Si c'est le cas, vous pouvez utiliser le code original (voir les modifications) et utiliser simplement l'interface comme type reflété. –
Oui, la propriété est sur l'interface. J'ai essayé cela avec votre exemple original (avant les modifications, si c'est ce que vous voulez dire?), Mais j'ai eu une exception de ne pas pouvoir lier le délégué - ou quelque chose comme ça - je dois réessayer d'obtenir le message d'exception . – asgerhallas