【问题标题】:TypeDescriptor.GetProperties(thisType) to return properties, which are write-onlyTypeDescriptor.GetProperties(thisType) 返回属性,这是只写的
【发布时间】:2009-04-21 11:17:36
【问题描述】:

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

/阿斯格

【问题讨论】:

  • 恢复你的cmets;对于您不知道类型的情况,我将发布带有“技巧”的更新...
  • 其实我只是重读了你的cmets;是接口上的只写属性吗?如果是这样,您可以使用原始代码(请参阅编辑)并将接口用作反射类型。
  • 是的,该属性在界面上。我用您的原始示例进行了尝试(在编辑之前,如果这就是您的意思吗?),但是我遇到了一个关于无法绑定委托的异常 - 或类似的东西 - 我必须再次尝试获取实际的异常消息.

标签: c# propertydescriptor typedescriptor


【解决方案1】:

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

【讨论】:

  • 嗨,马克,非常感谢您的及时回复! setter 作为代理来配置 webforms 应用程序中的一些视图元素。演示者将通过视图接口在视图上设置所需的配置 - 它使用接口上的反射和视图实现最终使用 SetValue 在视图上设置配置。随着我们系统的增长,并且有很多视图(相互嵌套),反射部分变得非常缓慢。
  • 我认为您的委托解决方案对我来说似乎是正确的方法 - 我将 SetValue 的逻辑隐藏在一个普通类中。我马上去试试看。是否有更多关于您推荐的其他可能性的文档/“入门文章”?
  • 我刚刚仔细看了看。委托解决方案的一个问题是,我在编译时不知道视图实现(您的 Foo)的类型,只知道 Foo 实现的接口。在运行时我有类型。然后我应该使用反射来创建委托 - 我不知道这是否会破坏性能......
  • 哇。这是令人印象深刻的答案。非常感谢你!我刚刚在系统的一部分中实现了您的表达式解决方案,即使没有缓存委托,“SetValue”的分析运行时间也减半。非常感谢你! PS。我还没有足够的代表来对您的答案进行投票。我稍后再做!
【解决方案2】:

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

【讨论】:

  • 仅供参考:只有无参数版本返回所有公共属性。有一个重载需要一些 BindingFlags,如果需要,您可以使用它们来获取所有属性。
  • 感谢您的帮助。但我确实需要 PropertyDescriptor 而不是 PropertyInfo,因为我正在使用 Marc Gravell HyperTypeDescriptor 因为 og 性能问题。所以不幸的是,使用 Type.GetProperties 不是一个解决方案。
  • 哇...如果只是调用一个setter方法那么快:-) 感谢您的回复,我现在就去阅读,很快就会回复!
猜你喜欢
  • 1970-01-01
  • 2017-11-20
  • 1970-01-01
  • 2016-12-05
  • 1970-01-01
  • 1970-01-01
  • 2023-03-05
  • 2016-04-28
  • 2023-03-27
相关资源
最近更新 更多