Matt
Matt

Reputation: 41842

How do you find only properties that have both a getter and setter?

C#, .NET 3.5

I am trying to get all of the properties of an object that have BOTH a getter and a setter for the instance. The code I thought should work is

PropertyInfo[] infos = source.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.SetProperty | BindingFlags.GetProperty);

However, the results include a property that does not have a setter. To give you a simple idea of my inheritance structure that might be affecting this (though I don't know how):

public interface IModel
{
    string Name { get; }
}

public class BaseModel<TType> : IModel
{
    public virtual string Name { get { return "Foo"; } }

    public void ReflectionCopyTo(TType target)
    {
        PropertyInfo[] infos = this.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.SetProperty | BindingFlags.GetProperty);
        foreach (PropertyInfo info in infos)
            info.SetValue(target, info.GetValue(this, null), null);
    }
}

public class Child : BaseModel<Child>
{
    // I do nothing to override the Name property here
}

I end up with the following error when working with Name:

System.ArgumentException: Property set method not found.

EDIT: I would like to know why this does not work, as well as what I should be doing to not get the error.

Upvotes: 42

Views: 16095

Answers (5)

Tim Jarvis
Tim Jarvis

Reputation: 18825

how about...

var qry = typeof(Foo).GetProperties(BindingFlags.Instance | BindingFlags.Public)
            .Where(p => p.CanRead && p.CanWrite);

Upvotes: 39

Rune Sundling
Rune Sundling

Reputation:

This isn't supposed to work.

See definition of GetProperties on msdn for which is allowed:

The following BindingFlags filter flags can be used to define which nested types to include in the search:

* You must specify either BindingFlags.Instance or BindingFlags.Static in order to get a return.
* Specify BindingFlags.Public to include public properties in the search.
* Specify BindingFlags.NonPublic to include non-public properties (that is, private and protected members) in the search.
* Specify BindingFlags.FlattenHierarchy to include static properties up the hierarchy.

Or you can see the definition of GetProperty/SetProperty in msdn, which states that:

GetProperty = Specifies that the value of the specified property should be returned.

SetProperty = Specifies that the value of the specified property should be set. For COM properties, specifying this binding flag is equivalent to specifying PutDispProperty and PutRefDispProperty.

Upvotes: 1

Herman Schoenfeld
Herman Schoenfeld

Reputation: 8734

To make it a little more generic you can inherit from 'ObjectWithDefaultValues' and/or call obj.SetDefaultValues() extension method. Both are listed below.

Code:

public abstract class ObjectWithDefaultValues : object {

    public ObjectWithDefaultValues () : this(true){
    }

    public ObjectWithDefaultValues (bool setDefaultValues) {
        if (setDefaultValues)
            this.SetDefaultValues();    
    }
}

public static class ObjectExtensions {

    public static void SetDefaultValues(this object obj) {
        foreach (FieldInfo f in obj.GetType().GetFields(BindingFlags.Instance | BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.SetField)) {
            foreach (Attribute attr in f.GetCustomAttributes(true)) {
                if (attr is DefaultValueAttribute) {
                    var dv = (DefaultValueAttribute)attr;
                    f.SetValue(obj, dv.Value);
                }
            }
        }

        foreach (var p in obj.GetType().GetProperties(BindingFlags.Instance | BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.SetProperty)) {
            if (p.GetIndexParameters().Length == 0) {
                foreach (Attribute attr in p.GetCustomAttributes(true)) {
                    if (attr is DefaultValueAttribute) {
                        var dv = (DefaultValueAttribute)attr;
                        p.SetValue(obj, dv.Value, null);
                    }
                }
            }
        }
    }
}

Upvotes: 0

You can check the PropertyInfo.CanRead and PropertyInfo.CanWrite properties.

Upvotes: 43

Jon Skeet
Jon Skeet

Reputation: 1503599

Call GetGetMethod and GetSetMethod on the property - if both results are non-null, you're there :)

(The parameterless versions only return public methods; there's an overload with a boolean parameter to specify whether or not you also want non-public methods.)

Upvotes: 49

Related Questions