关于c#:如何通过反射在单个调用中获取字段和属性?

how to get both fields and properties in single call via reflection?

如果在某个地方有这个问题,我很抱歉。我在发帖前做了调查!

好的,那么问题…我正在使用gettype().get properties,但它没有返回简单的实例字段,这些字段上没有get/set…所以我使用了.get fields,它找到了它们,但我想让一个简单的单个对象在不在字段和属性之间切换的情况下获取/设置一个值…这可能吗?

我当前的代码适用于propertyinfo,这很好用,但我想这不是针对字段吗?

[编辑]这是我提出的解决方案,效果很好。谢谢大家……

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
    // 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");
        }
    }


怎么样:

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

或者,像fastmember这样的库可以很好地处理字段或属性,无论成员类型如何,get/set都是相同的。


getProperties()和getFields()的返回类型是不同的,您似乎已经注意到了。您必须使用getValue()和setValue()定义一个接口,并使用extend-parameterInfo和fieldinfo来实现这个接口。这可能起到包装的作用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
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();
    }
}

这将允许您执行GetType().GetParametersAndFields()(即使用标准反射类型)。


有点晚了,但我想出了以下几点……1个循环,像个魔咒一样工作;-)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
        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);
            }
        }

要获取属性或字段,可以说:

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

其中bindingAttr可以

1
2
3
4
var bindingAttr=
        BindingFlags.NonPublic|
        BindingFlags.Public|
        BindingFlags.Instance;

如果您不想获得非公开成员,请删除BindingFlags.NonPublic。顺便说一下,查询不是单个调用,而是单个语句。

要在不自己强制转换的情况下获取属性或字段的值,请使用InvokeMember进行技巧:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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);
}

同样,要设置该值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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);
}

如果你通过了除PropertyInfoFieldInfo以外的MemberInfo作为第一个论点,它会抛出,因为BindingFlags.Default没有指明你要做什么。


使用dlr(足够简单,如果您在编译时知道成员名):

1
((dynamic)obj).MyFieldOrPropertyName = myValue;

如果你在运行时只知道成员的名字,我推荐fastmember,正如MarcGravell所建议的。