26

I apologize if this is covered somewhere. I did research before posting!

okay, so question...I'm using GetType( ).GetProperties, but it isn't returning simple instance fields, which don't have get/set on them...so I used .GetFields, which finds them, but I want to get a simple single object to get/set a value on without flipping between fields and properties...is this possible?

my current code works on PropertyInfo, which is working great, but that isn't for fields I guess?

[edit] this is the solution I came up with, which is working good. thanks everyone....

    // some logic borrowed from James Newton-King, http://www.newtonsoft.com
    public static void SetValue(this MemberInfo member, object property, object value)
    {
        if (member.MemberType == MemberTypes.Property)
            ((PropertyInfo)member).SetValue(property, value, null);
        else if (member.MemberType == MemberTypes.Field)
            ((FieldInfo)member).SetValue(property, value);
        else
            throw new Exception("Property must be of type FieldInfo or PropertyInfo");
    }

    public static object GetValue(this MemberInfo member, object property)
    {
        if (member.MemberType == MemberTypes.Property)
            return ((PropertyInfo)member).GetValue(property, null);
        else if (member.MemberType == MemberTypes.Field)
            return ((FieldInfo)member).GetValue(property);
        else
            throw new Exception("Property must be of type FieldInfo or PropertyInfo");
    }

    public static Type GetType(this MemberInfo member)
    {
        switch (member.MemberType)
        {
            case MemberTypes.Field:
                return ((FieldInfo)member).FieldType;
            case MemberTypes.Property:
                return ((PropertyInfo)member).PropertyType;
            case MemberTypes.Event:
                return ((EventInfo)member).EventHandlerType;
            default:
                throw new ArgumentException("MemberInfo must be if type FieldInfo, PropertyInfo or EventInfo", "member");
        }
    }
Timmerz
  • 6,090
  • 5
  • 36
  • 49

6 Answers6

38

How about:

const BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Instance;
MemberInfo[] members = type.GetFields(bindingFlags).Cast<MemberInfo>()
    .Concat(type.GetProperties(bindingFlags)).ToArray();

Alternatively, libraries like FastMember will work happily with either fields or properties, with get/set identical regardless of the member-type.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • From OP: "get a simple single object to get/set a value". I'm not sure how MemberInfo does that. – CrazyCasta Oct 01 '12 at 20:22
  • 1
    @CrazyCasta indeed; if you want that, you need to step outside the core reflection API as there *is* no single shared interface for that. However, separate libraries do exist, which is why I cited FastMember. – Marc Gravell Oct 01 '12 at 20:27
  • 1
    I really would like to mark both @CrazyCasta and marc as answers as they are both helpful, but ultimately I did go with the MemberInfo using extension methods. – Timmerz Oct 02 '12 at 00:12
11

The return types of GetProperties() and GetFields() are different, as you seem to have noticed. You would have to define an interface with the GetValue() and SetValue() and use extend ParameterInfo and FieldInfo to implement this interface. This would probably work as a wrapper:

interface IGetSettable
{
    public void SetValue(
        Object obj,
        Object value,
        Object[] index);
    public Object GetValue(
        Object obj,
        Object[] index);
}

public class ParameterInfoGS : IGetSettable
{
    protected ParameterInfo pi;

    public ParameterInfoExtra(ParameterInfo _pi)
    {
        pi = _pi;
    }

    public void SetValue(
        Object obj,
        Object value,
        Object[] index) {pi.SetValue(obj, value, index);}
    public Object GetValue(
        Object obj,
        Object[] index) {return pi.GetValue(obj, index);}
}

public class FieldInfoGS : IGetSettable
{
    protected FieldInfo pi;

    public FieldInfoExtra(FieldInfo _pi)
    {
        pi = _pi;
    }

    public void SetValue(
        Object obj,
        Object value,
        Object[] index) {pi.SetValue(obj, value, index);}
    public Object GetValue(
        Object obj,
        Object[] index) {return pi.GetValue(obj, index);}
}

public static class AssemblyExtension
{
    public static IGetSettable[] GetParametersAndFields(this Type t)
    {
        List<IGetSettable> retList = new List<IGetSettable>();

        foreach(ParameterInfo pi in t.GetParameters())
            retList.Add(new ParameterInfoExtra(pi));

        foreach(FieldInfo fi in t.GetFields())
            retList.Add(new FieldInfoExtra(fi));

        return retList.ToArray();
    }
}

This will allow you to do GetType().GetParametersAndFields() (i.e. use the standard reflection types).

CrazyCasta
  • 26,917
  • 4
  • 45
  • 72
7

A bit late, but I came up with the following ... 1 loop, works like a charm ;-)

        MemberInfo[] memberInfos = dotNetType.GetMembers();
        ModelPropertySpec modelPropertySpec;
        foreach (MemberInfo memberInfo in memberInfos)
        {
            Type itemType = null;
            String memberName = memberInfo.Name;
            switch (memberInfo.MemberType)
            {
                case MemberTypes.Property:
                    itemType = dotNetType.GetProperty(memberName).PropertyType;
                    break;
                case MemberTypes.Field:
                    itemType = dotNetType.GetField(memberName).FieldType;
                    break;
            }

            if (itemType != null)
            {
                modelPropertySpec = ParsePropertyType(memberName, itemType);
                modelSpec.Properties.Add(modelPropertySpec.Name, modelPropertySpec);
            }
        }
Paul0515
  • 23,515
  • 9
  • 32
  • 47
5

To get either properties or fields, you can say:

var q=
    from it in type.GetMembers(bindingAttr)
    where it is PropertyInfo||it is FieldInfo
    select it;

where bindingAttr could be

var bindingAttr=
        BindingFlags.NonPublic|
        BindingFlags.Public|
        BindingFlags.Instance;

Remove BindingFlags.NonPublic if you don't want to get non-public members. By the way, the query is not a single call but a single statement.


To get the value of either a property or a field without casting it by yourself, use InvokeMember for the trick:

static object GetValue<T>(
        T x, object target) where T:MemberInfo {
    var invokeAttr=(
            x is FieldInfo
                ?BindingFlags.GetField
                :x is PropertyInfo
                    ?BindingFlags.GetProperty
                    :BindingFlags.Default)|
            BindingFlags.NonPublic|
            BindingFlags.Public|
            BindingFlags.Instance;

    return target.GetType().InvokeMember(
        x.Name, invokeAttr, default(Binder), target, null);
}

Similarly, to set the value:

static void SetValue<T>(
        T x, object target, object value) where T:MemberInfo {
    var args=new object[] { value };
    var invokeAttr=(
            x is FieldInfo
                ?BindingFlags.SetField
                :x is PropertyInfo
                    ?BindingFlags.SetProperty
                    :BindingFlags.Default)|
            BindingFlags.NonPublic|
            BindingFlags.Public|
            BindingFlags.Instance;

    target.GetType().InvokeMember(
        x.Name, invokeAttr, default(Binder), target, args);
}

It will throw if you pass a MemberInfo other than PropertyInfo or FieldInfo as the first argument, because BindingFlags.Default doesn't specify what you are going to do.

Ken Kin
  • 4,503
  • 3
  • 38
  • 76
3

Using DLR (simple enough, if you know the member name at compile time):

((dynamic)obj).MyFieldOrPropertyName = myValue;

If you only know the member name at run-time, I'd recommend FastMember, as Marc Gravell suggested.

Eren Ersönmez
  • 38,383
  • 7
  • 71
  • 92
1

I was wanting something similar to this for something I'm working on, and I wanted a better generic way to handle it.

I tested a lot of ways but I settled on making a generic function that could be used to get any type of member, and then just calling it for properties and fields to get the value members.

Getting the Members

public static MemberInfo[] GetValueMembers(this Type t, BindingFlags bindings)
{
    MemberInfo[] result = t.GetMembersSlow(bindings, MemberTypes.Field | MemberTypes.Property);
    return result;
}

public static MemberInfo[] GetMemberSlow(
    this Type t, BindingFlags bindings, MemberTypes types
){
    MemberInfo[] result = t.FindMembers(types, bindings, (o, p) => true, null);
    return result;
}

This offers okay performance, not stellar, but better than some other ways I tried. The IEnumerable<MemberInfo>.Concat() approach of casting results of GetProperties() and GetMethods() was particularly slow comparatively.

But, I got really good performance by doing GetProperties() and GetFields() and using Array.Copy, so I decided to see if I could write something that uses all the GetX() types from Type based on what you request and then Array.Copy() all the results together.

public static MemberInfo[] GetMembers(this Type t, BindingFlags bindings, MemberTypes types)
{
    List<MemberInfo[]> resultArrays = new List<MemberInfo[]>();

    MemberTypes[] memberTypes = types.Split();
    MemberInfo[] buffer = null;

    int totalLength = 0;

    foreach (MemberTypes memberType in memberTypes)
    {
        switch (memberType)
        {
            case MemberTypes.Constructor:
                buffer = t.GetConstructors(bindings);
                break;

            case MemberTypes.Event:
                buffer = t.GetEvents(bindings);
                break;

            case MemberTypes.Field:
                buffer = t.GetFields(bindings);
                break;

            case MemberTypes.Method:
                buffer = t.GetMethods(bindings);
                break;

            case MemberTypes.Property:
                buffer = t.GetProperties(bindings);
                break;

            default:
                // maybe do something here? the other values don't really
                // make sense in a GetMembers call, probably ignore them.
                break;
        }

        if (buffer != null && buffer.Length > 0)
        {
            resultArrays.Add(buffer);
            totalLength += buffer.Length;
            buffer = null;
        }
    }

    MemberInfo[] result = new MemberInfo[totalLength];

    int currentIndex = 0;
    foreach (MemberInfo[] resultArray in resultArrays)
    {
        if (resultArray.Length == 0)
            continue;

        Array.Copy(resultArray, 0, result, currentIndex, resultArray.Length);
        currentIndex += resultArray.Length;
    }

    return result;
}

There's a bit hidden here, in the types.Split() call, that's something I wrote that takes a flags enum and spits out an array of all the individual flags that were contained in it so it's easy to loop over. See https://stackoverflow.com/a/72251903/832859 for the Split() code.

Oh, this is kind of important, and I didn't get it at first: MemberInfo is an abstract base type, and all the XInfo types are derived from it, and easily castable to and from. So to change, say, a ConstructorInfo[] to a MemberInfo[], just assign it to a MemberInfo[] variable, or cast it with (MemberInfo[]) or X as MemberInfo[], etc.

Depending on what's passed in, this function will grab all the relevant MemberInfo arrays from the Type based on the bindings/membertypes you've passed in, grabs them individually from the specific functions, and then copies them all into one big MemberInfo[]. As I mentioned I got good performance on the Properties+Fields, but I was worried extra overhead from so many of these would slow it down.

Turns out my worries were unfounded, it outperformed everything else I tried, by a pretty good margin. This code is all marginally tested out and working. I tested the two remaining GetMembers() on Forms.GetType() and it matches what's returned by Type.FindMembers().

Using the members

Lastly, I wrote some helpers for actually making use of the Properties+Fields data. Since MemberInfo is abstract, there's no convenient way to get values from instances of the members.

When you have a FieldInfo or PropertyInfo that you want to get information from, or values from its instances, you just create a MemberFactory, passing in the field or property info as a MemberInfo.

If you need the type of the member, you call GetMemberSystemType() which returns the System.Type of the member.

NB: This probably doesn't handle all cases by a long shot, but it works for just basic members.

public struct MemberFactory
{
    public readonly FieldInfo fieldInfo;
    public readonly PropertyInfo propertyInfo;

    private MemberFactory(MemberInfo memberInfo)
    {
        fieldInfo = memberInfo as FieldInfo;
        propertyInfo = memberInfo as PropertyInfo;

        if (fieldInfo == null && propertyInfo == null)
            throw new ArgumentException("memberInfo must derive from FieldInfo or PropertyInfo", memberInfo.Name);
    }

    public Type GetMemberSystemType()
    {
        Type result = null;

        if (IsProperty)
            result = propertyInfo.PropertyType;
        else if (IsField)
            result = fieldInfo.FieldType;

        return result;
    }

    public bool IsProperty { get => propertyInfo != null; }
    public bool IsField { get => fieldInfo != null; }

    public Member MakeMember(object instance)
    {
        return new Member(instance, this);
    }

    public static MemberFactory Create(MemberInfo memberInfo)
    {
        return new MemberFactory(memberInfo);
    }
}

Then, when you have an actual instance you want to deal with, just call MakeMember() with the instance to get a Member object to act on that instance.

Since we've got both a property and a field to have to check, in the implementation, I just used the null conditional/null coalescing to check them both. I didn't test whether there was much of a performance hit over just checking IsMethod()/IsProperty() on the MemberFactory but I think it's fine.

And finally, a couple of extensions functions so that if you have a MemberInfo and an instance, you don't have to create a MemberFactory/Member to use them, in case you just just need to access a value quickly without a lot of set up.

public class Member
{
    object instance;
    MemberFactory factory;

    public Member(object instance, MemberFactory factory)
    {
        this.instance = instance;
        this.factory = factory;
    }

    public object GetValue()
    {
        return 
            factory.fieldInfo?.GetValue(instance) 
            ?? factory.propertyInfo?.GetValue(instance);
    }

    public void SetValue(object value)
    {
        factory.fieldInfo?.SetValue(instance, value);
        factory.propertyInfo?.SetValue(instance, value);
    }
}

public static void SetValue(this MemberInfo memberInfo, object instance, object value)
{
    MemberFactory.Create(memberInfo).MakeMember(instance).SetValue(value);
}

public static object GetValue(this MemberInfo memberInfo, object instance)
{
    return MemberFactory.Create(memberInfo).MakeMember(instance).GetValue();
}

So, that's about it. I just was needing this for something else and this was one of the pages I came across when I was searching for help, so I noted it and came back once I had some sort of solid semi-tested ideas worked out.

The kernel of an idea that made me work on this was pretty much just what's in the Member.SetValue()call.

cigien
  • 57,834
  • 11
  • 73
  • 112
shelleybutterfly
  • 3,216
  • 15
  • 32