Search code examples
c#propertydescriptortypedescriptor

TypeDescriptor.GetProperties(thisType) to return properties, which are write-only


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


Solution

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