我是否可以创建一个泛型方法,该方法采用值类型或引用类型,但始终返回可为 null 的类型
本文关键字:类型 引用类型 null 返回 方法 创建 是否 泛型方法 一个 | 更新日期: 2023-09-27 18:36:41
这是我的方法。请注意,我返回泛型参数 R
的等效可为 null 类型:
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"
无法将类型"字符串"隐式转换为"字符串"
我可以使用任何技巧来在此处获取相同的方法签名,但让泛型将返回类型推断为可以为 null 的类型?
这是一些测试代码,用于显示它适用于整数值:
//[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;
}
}
不,没有单独的签名可以做到这一点 - 没有办法说"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
开头,例如TInput
和TResult
- 为什么要使用表达式树进行
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);
}