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

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

这可能吗?

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


当前回答

使用微软的CallByName怎么样?VisualBasic命名空间(Microsoft.VisualBasic.dll)?它使用反射来获取普通对象、COM对象甚至动态对象的属性、字段和方法。

using Microsoft.VisualBasic;
using Microsoft.VisualBasic.CompilerServices;

然后

Versioned.CallByName(this, "method/function/prop name", CallType.Get).ToString();

其他回答

下面的方法非常适合我:

class MyClass {
    public string prop1 { set; get; }

    public object this[string propertyName]
    {
        get { return this.GetType().GetProperty(propertyName).GetValue(this, null); }
        set { this.GetType().GetProperty(propertyName).SetValue(this, value, null); }
    }
}

获取属性值:

MyClass t1 = new MyClass();
...
string value = t1["prop1"].ToString();

设置属性值。

t1["prop1"] = value;

如果我用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);
}

这样怎么样:

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

        Type type = obj.GetType();
        PropertyInfo info = type.GetProperty(part);
        if (info == null) { return null; }

        obj = info.GetValue(obj, null);
    }
    return obj;
}

public static T GetPropValue<T>(this Object obj, String name) {
    Object retval = GetPropValue(obj, name);
    if (retval == null) { return default(T); }

    // throws InvalidCastException if types are incompatible
    return (T) retval;
}

这将允许你使用单个字符串进入属性,就像这样:

DateTime now = DateTime.Now;
int min = GetPropValue<int>(now, "TimeOfDay.Minutes");
int hrs = now.GetPropValue<int>("TimeOfDay.Hours");

您可以将这些方法用作静态方法或扩展。

虽然最初的问题是关于如何仅使用一个字符串作为参数来获取属性的值,但是在这里使用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.为这段代码的初始版本:)

public static List<KeyValuePair<string, string>> GetProperties(object item) //where T : class
    {
        var result = new List<KeyValuePair<string, string>>();
        if (item != null)
        {
            var type = item.GetType();
            var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
            foreach (var pi in properties)
            {
                var selfValue = type.GetProperty(pi.Name).GetValue(item, null);
                if (selfValue != null)
                {
                    result.Add(new KeyValuePair<string, string>(pi.Name, selfValue.ToString()));
                }
                else
                {
                    result.Add(new KeyValuePair<string, string>(pi.Name, null));
                }
            }
        }
        return result;
    }

这是一种在List中获取所有属性及其值的方法。