2

这是我的方法。请注意,我正在为泛型参数返回等效的可为空类型R

    public static Nullable<R> GetValue<T, R>(this T a, Expression<Func<T, R>> expression)
        where T : Attribute
        where R : struct
    {
        if (a == null)
            return null;

        PropertyInfo p = GetProperty(expression);
        if (p == null)
            return null;

        return (R)p.GetValue(a, null);
    }

我可以在调用中使用它来获取如下属性的值:

//I don't throw exceptions for invalid or missing calls 
//because I want to chain the calls together:
int maximumLength4 = instance.GetProperty(x => x.ToString())
                            .GetAttribute<StringLengthAttribute>()
                            .GetValue(x => x.MaximumLength)
                            .GetValueOrDefault(50);

我想对字符串使用相同的通用方法:

//I'd like to use the GetValue generic method with strings as well as integers 
string erroMessage = instance.GetProperty(x => x.ToString())
                            .GetAttribute<StringLengthAttribute>()
                            .GetValue(x => x.ErrorMessage);

但它不会编译:

类型“R”必须是不可为空的值类型,才能将其用作泛型类型或方法“System.Nullable”中的参数“T”

无法隐式转换类型“字符串?” 到“字符串”

有什么技巧我可以在这里获得相同的方法签名,但让泛型推断返回类型可以为空?

这是一些测试代码,表明它适用于整数值:

//[StringLength(256)]
//public string Name { get; set; }
PropertyInfo info = ReflectionAPI.GetProperty<Organisation, String>(x => x.Name);//not null
StringLengthAttribute attr = info.GetAttribute<StringLengthAttribute>();//not null
int? maximumLength = attr.GetValue(x => x.MaximumLength);//256
int? minimumLength = attr.GetValue(x => x.MinimumLength);//0

PropertyInfo info2 = ReflectionAPI.GetProperty<Organisation, int>(x => x.ID);//not null
StringLengthAttribute attr2 = info2.GetAttribute<StringLengthAttribute>();//null because ID doesn't have the attribute
int? maximumLength2 = attr2.GetValue(x => x.MaximumLength);//null
int? minimumLength2 = attr2.GetValue(x => x.MinimumLength);//null

//I can use the GetProperty extension method on an instance
Organisation instance = (Organisation)null;
PropertyInfo info3 = instance.GetProperty(x => x.ToString());//null because its a method call not a property
StringLengthAttribute attr3 = info3.GetAttribute<StringLengthAttribute>();//null
int? maximumLength3 = attr3.GetValue(x => x.MaximumLength);//null
int? minimumLength3 = attr3.GetValue(x => x.MinimumLength);//null

这是我的其余部分ReflectionAPI

public static class ReflectionAPI
{

    public static Nullable<R> GetValue<T, R>(this T a, Expression<Func<T, R>> expression)
        where T : Attribute
    {
        if (a == null)
            return null;

        PropertyInfo p = GetProperty(expression);
        if (p == null)
            return null;

        return (R)p.GetValue(a, null);
    }

    public static T GetAttribute<T>(this PropertyInfo p) where T : Attribute
    {
        if (p == null)
            return null;

        return p.GetCustomAttributes(false).OfType<T>().LastOrDefault();
    }

    public static PropertyInfo GetProperty<T, R>(Expression<Func<T, R>> expression)
    {
        if (expression == null)
            return null;

        MemberExpression memberExpression = expression.Body as MemberExpression;
        if (memberExpression == null)
            return null;

        return memberExpression.Member as PropertyInfo;
    }
}
4

1 回答 1

5

不,没有单独的签名可以做到这一点 - 没有办法说“ 的可空类型R,它本身要么R是引用类型,要么Nullable<R>是不可为空的值类型”。

您可以有不同的方法,每种方法都有不同的约束R- 但是您要么必须提供不同的名称,要么使用可怕的黑客有效地通过类型参数约束重载。

为简单起见,我怀疑您在这里基本上应该有两个不同的方法名称。所以签名:

public static Nullable<R> GetValue<T, R>(this T a, Expression<Func<T, R>> expression)
    where T : Attribute
    where R : struct

public static R GetReference<T, R>(this T a, Expression<Func<T, R>> expression)
    where T : Attribute
    where R : class

两个旁白:

  • 通常类型参数以T, 例如TInputTResult
  • 你为什么要使用表达式树GetValue呢?为什么不直接Func<T, R>执行它呢?

作为第二个要点的示例:

public static Nullable<R> GetValue<T, R>(this T a, Func<T, R> func)
    where T : Attribute
    where R : struct
{
    if (a == null)
        return null;

    return func(a);
}
于 2013-10-24T16:17:18.420 回答