TypeDescriptor.GetProperties(thisType) 返回属性,这些属性是只写的

发布于 2024-07-18 03:39:04 字数 165 浏览 11 评论 0原文

我试图从类型中获取所有属性,但使用 TypeDescriptor.GetProperties(thisType) 只会为我提供具有 setter 和 getter 的属性。 我有只写属性。 有没有办法检索包括这些在内的 PropertyDescriptorCollection?

/阿斯格

I'm trying to get all properties from a type, but using TypeDescriptor.GetProperties(thisType) will only supply me with properties, that has both setter and a getter. I have write-only properties. Are there a way to retrieve a PropertyDescriptorCollection including those?

/Asger

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

吃→可爱长大的 2024-07-25 03:39:04

只写属性是一种罕见的野兽,并且不存在于 System.ComponentModel / PropertyDescriptor 空间中。 PropertyDescriptor 被设计为可读的。 我可能可以破解 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();
    }
}

Write-only properties are a rare beast, and don't exist in the System.ComponentModel / PropertyDescriptor space. PropertyDescriptors are designed to be readable. I could probably hack HyperDescriptor to shim write-only properties, but it would be a hack - and it would presumably have to throw exceptions for get, which could impact calling code quite a bit.

As an aside; I generally advise against write-only properties; the text-book example that people trot out is passwords (public string Password {private get;set;}) - I'd much rather have a void SetPassword(string newPassword) method...

What is it that you actually want to do? There are a range of options here, all very achievable:

  • use reflection alone (slow; maybe not an option)
  • use Delegate.CreateDelegate (very easy)
  • use Expression.Compile (a little harder, but not much)
  • use Reflection.Emit (quite hard)
  • shim write-only properties into PropertyDescriptor (quite hard)

If you let me know what you actually want to do (rather than the way you are currently trying to do it), I might be able to help more.

As an example using Delegate.CreateDelegate (note you would want to stash the delegate somewhere and re-use it lots of times):

edited to show how to do it if you don't know the specific types at runtime

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);
    }
}

Or alternatively using the 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();
    }
}
姐不稀罕 2024-07-25 03:39:04

使用 System.Type.GetProperties() 相反,它返回所有属性。 请注意,这会返回 PropertyInfo[] 而不是 PropertyDescriptorCollection

Use System.Type.GetProperties() instead, that returns all properties. Notice that this returns a PropertyInfo[] instead of a PropertyDescriptorCollection.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文