TypeDescriptor.GetProperties(thisType) は書き込み専用のプロパティを返します。
-
12-09-2019 - |
質問
型からすべてのプロパティを取得しようとしていますが、TypeDescriptor.GetProperties(thisType) を使用すると、セッターとゲッターの両方を持つプロパティのみが提供されます。書き込み専用のプロパティがあります。それらを含むPropertyDescriptorCollectionを取得する方法はありますか?
/アスガー
解決
書き込み専用プロパティは非常にまれで、System.ComponentModel / PropertyDescriptor 空間には存在しません。 PropertyDescriptor
は読みやすいように設計されています。おそらくハッキングできるだろう HyperDescriptor
書き込み専用プロパティをシム化しますが、それはハックになります - そしておそらく例外をスローする必要があります get
, これは、コードの呼び出しにかなりの影響を与える可能性があります。
余談として;私は一般的にアドバイスします に対して 書き込み専用プロパティ。人々が持ち出す教科書の例はパスワードです (public string Password {private get;set;}
) - むしろ、 void SetPassword(string newPassword)
方法...
本当にやりたいことは何ですか?ここにはさまざまなオプションがあり、すべて非常に実現可能です。
- リフレクションのみを使用します (遅い;オプションではないかもしれません)
- 使用
Delegate.CreateDelegate
(非常に簡単) - 使用
Expression.Compile
( 少し より困難ですが、それほどではありません) - 使用
Reflection.Emit
(かなりきつい) - shim 書き込み専用プロパティ
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();
}
}
他のヒント
System.Type.GetProperties()
の代わりに、それはすべてのプロパティを返してください。これは代わりにPropertyInfo[]
のPropertyDescriptorCollection
を返すことに注意してください。