我可以创建一个采用值类型或引用类型但始终返回可为空类型的泛型方法吗 [英] Can I create a generic method that takes a value type or a reference type but always returns a nullable type

查看:32
本文介绍了我可以创建一个采用值类型或引用类型但始终返回可为空类型的泛型方法吗的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是我的方法.请注意,我正在返回通用参数 R 的等效可空类型:

This is my method. Note that I am returning the equivalent nullable type for the generic parameter 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 can use it in a call to get the value of an attribute like this:

//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 same generic method with strings:

//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"

The type 'R' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'System.Nullable'

不能隐式转换类型字符串?"到字符串"

Cannot implicitly convert type 'string?' to 'string'

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

Is there any trick I can use to get the same method signature here and yet get generics to infer the return type as one that can be null?

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

This is some test code to show that it works for integer values:

//[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 的其余部分:

And this is the rest of my 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;
    }
}

推荐答案

不,没有单独的签名可以做到这一点 - 没有办法说R 的可空类型,要么R 本身用于引用类型,或者 Nullable 用于不可为 null 的值类型".

No, there's no individual signature that can do this - there's no way of saying "the nullable type of R, which is either R itself for a reference type, or Nullable<R> for a non-nullable value type".

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

You can have different methods, each with a different constraint on R - but then you either have to provide different names, or use horrible hacks to effectively overload by type parameter constraints.

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

I suspect you should basically have two different method names here, for simplicity. So signatures of:

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 并执行它?
  • Conventionally type parameters start with T, e.g. TInput and TResult
  • Why are you using expression trees at all for GetValue? Why not just take a Func<T, R> and execute it?

以第二个要点为例:

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);
}

这篇关于我可以创建一个采用值类型或引用类型但始终返回可为空类型的泛型方法吗的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆