我试图在我的代码中使用Reflection1示例实现数据转换。

GetSourceValue函数有一个比较各种类型的开关,但我想删除这些类型和属性,并让GetSourceValue只使用一个字符串作为参数来获取属性的值。我想在字符串中传递一个类和属性,并解析属性的值。

这可能吗?

1原始博客文章的Web存档版本


当前回答

当你想要遍历对象中的所有属性,然后使用属性的每个值时,必须使用这段代码:

foreach (var property in request.GetType().GetProperties())
{
    var valueOfProperty = property.GetValue(properties, null);
}

其他回答

如果我用Ed s的密码,我就

“ReflectionExtensions。由于其保护级别,GetProperty(Type, string)'不可访问

似乎GetProperty()在Xamarin.Forms中不可用。TargetFrameworkProfile是我的便携式类库中的Profile7。NET Framework 4.5, Windows 8, ASP。NET Core 1.0, Xamarin。Android, Xamarin的。Xamarin的iOS。iOS经典)。

现在我找到了一个可行的解决方案:

using System.Linq;
using System.Reflection;

public static object GetPropValue(object source, string propertyName)
{
    var property = source.GetType().GetRuntimeProperties().FirstOrDefault(p => string.Equals(p.Name, propertyName, StringComparison.OrdinalIgnoreCase));
    return property?.GetValue(source);
}

使用系统的PropertyInfo。反射的名称空间。无论我们试图访问什么属性,反射编译都很好。该错误将在运行时出现。

    public static object GetObjProperty(object obj, string property)
    {
        Type t = obj.GetType();
        PropertyInfo p = t.GetProperty("Location");
        Point location = (Point)p.GetValue(obj, null);
        return location;
    }

它可以很好地获取对象的Location属性

Label1.Text = GetObjProperty(button1, "Location").ToString();

我们将得到Location: {X=71,Y=27} 我们还可以返回位置。X或位置。Y也一样。

虽然最初的问题是关于如何仅使用一个字符串作为参数来获取属性的值,但是在这里使用Expression而不是简单的字符串来确保调用者永远不会使用硬编码的属性名是很有意义的。下面是一行的用法:

public static class Utils
...
    public static TVal GetPropertyValue<T, TVal>(T t, Expression<Func<T, TVal>> x)
        => (TVal)((x.Body as MemberExpression)?.Member as PropertyInfo)!.GetValue(t);

...
    var val = Utils.GetPropertyValue(foo,  p => p.Bar);

下面是一个在可读性方面稍微好一点的错误处理版本:

public static TVal GetPropertyValue<T, TVal>(T t, Expression<Func<T, TVal>> x)
{
    var m = (x.Body as MemberExpression)?.Member;
    var p = m as PropertyInfo;

    if (null == p)
        throw new ArgumentException($"Unknown property: {typeof(T).Name}.{(m?.Name??"???")}");

    return (TVal)p.GetValue(t);
}

简而言之,您传递了一个读取属性的lambda表达式。lambda的主体(胖箭头右边的部分)是一个成员表达式,您可以从中获取成员名称并将其转换为PropertyInfo,前提是该成员实际上是一个Property,而不是(例如)一个方法。

在简短的版本中,null宽恕操作符——the !-告诉编译器PropertyInfo不会为空。这是一个很大的谎言,你将在运行时得到一个NullReferenceException。较长的版本提供了属性的名称(如果它设法获得该属性的话)。

PS:感谢Oleg G.为这段代码的初始版本:)

jheddings和AlexD都写了关于如何解析属性字符串的出色答案。我想把我的库也加入其中,因为我专门为此编写了一个库。

路径。CSharp的主类是Resolver。默认情况下,它可以解析属性、数组和字典条目。

举个例子,如果你有一个这样的物体

var o = new { Property1 = new { Property2 = "value" } };

想要获得Property2,你可以这样做:

IResolver resolver = new Resolver();
var path = "Property1.Property2";
object result = r.Resolve(o, path); 
//=> "value"

这是它可以解析的路径的最基本示例。如果你想看看它还能做什么,或者你可以如何扩展它,只需前往它的Github页面。

这是我的解决方案。它也适用于COM对象,并允许从COM对象访问集合/数组项。

public static object GetPropValue(this object obj, string name)
{
    foreach (string part in name.Split('.'))
    {
        if (obj == null) { return null; }

        Type type = obj.GetType();
        if (type.Name == "__ComObject")
        {
            if (part.Contains('['))
            {
                string partWithoundIndex = part;
                int index = ParseIndexFromPropertyName(ref partWithoundIndex);
                obj = Versioned.CallByName(obj, partWithoundIndex, CallType.Get, index);
            }
            else
            {
                obj = Versioned.CallByName(obj, part, CallType.Get);
            }
        }
        else
        {
            PropertyInfo info = type.GetProperty(part);
            if (info == null) { return null; }
            obj = info.GetValue(obj, null);
        }
    }
    return obj;
}

private static int ParseIndexFromPropertyName(ref string name)
{
    int index = -1;
    int s = name.IndexOf('[') + 1;
    int e = name.IndexOf(']');
    if (e < s)
    {
        throw new ArgumentException();
    }
    string tmp = name.Substring(s, e - s);
    index = Convert.ToInt32(tmp);
    name = name.Substring(0, s - 1);
    return index;
}