如何在不装箱的情况下检查泛型参数是否不为空
How to check if a generic parameter is not null without boxing
我想编写一个接受单个 IBar
并调用其 Baz
方法的方法。
当 obj
为 null 时抛出:
void Foo<T>(T obj)
where T : IBar
=> obj.Baz();
当它是值类型时,此框 obj
:
void Foo<T>(T obj)
where T : IBar
=> obj?.Baz();
如果 obj
为零,则不会调用 Baz
:
void Foo<T>(T obj)
where T : IBar
{
if (!EqualityComparer<T>.Default.Equals(obj, default(T)))
obj.Baz();
}
在这里,Foo(new Bar())
总是选择通用方法,无论 Bar
是 class 还是结构:
void Foo<T>(T obj)
where T : struct, IBar
=> obj.Baz();
void Foo(IBar obj)
=> obj?.Baz();
这让我眼睛流血:
void FooVal<T>(T obj)
where T : struct, IBar
=> obj.Baz();
void FooRef(IBar obj)
=> obj?.Baz();
那么对此有最佳实践吗?我愿意接受所有建议。
编辑:
该问题被标记为 Generic constraints, where T : struct and where T : class 重复,并且使用的是旧标题。所以我更新了标题以更好地传达我的问题。我要问的是,如何调用泛型方法并仅在参数不为 null 时才使用参数 ,无需装箱。
可以使用针对链接问题 解释的一些解决方法 来回答这个问题,但我仍然认为这是一个根本不同的问题。
我想出了这个帮手:
public static class Null
{
public static bool IsNull<T>(T obj) => !Data<T>.HasValue(obj);
private static class Data<T>
{
public static readonly Func<T, bool> HasValue = InitHasValue();
private static Func<T, bool> InitHasValue()
{
var type = typeof(T);
if (!type.IsValueType)
return obj => obj != null;
if (type.IsGenericType &&
type.GetGenericTypeDefinition() == typeof(Nullable<>))
{
var value = Expression.Parameter(type, "value");
var getter = type.GetProperty("HasValue").GetMethod;
var call = Expression.Call(value, getter);
var lambda = Expression.Lambda<Func<T, bool>>(call, value);
return lambda.Compile();
}
return obj => true;
}
}
}
所以我可以这样做:
void Foo<T>(T obj)
where T : IBar
{
if (!Null.IsNull(obj)) // Does not box.
obj.Baz();
}
刚遇到类似的问题,我想到了以下解决方案:
void Foo<T>(T obj)
{
if (obj is object)
obj.Baz();
}
如果 obj
为 null 或值为 null
的可空类型,则 is
操作的结果始终为 false
,如果不是,则检查是否存在从 T
到 object
的装箱转换,情况总是如此。
除非编译器根本没有优化,否则这实际上应该只是一个 null
检查,或者是对不可空类型的 noop。
我想编写一个接受单个 IBar
并调用其 Baz
方法的方法。
当 obj
为 null 时抛出:
void Foo<T>(T obj)
where T : IBar
=> obj.Baz();
当它是值类型时,此框 obj
:
void Foo<T>(T obj)
where T : IBar
=> obj?.Baz();
如果 obj
为零,则不会调用 Baz
:
void Foo<T>(T obj)
where T : IBar
{
if (!EqualityComparer<T>.Default.Equals(obj, default(T)))
obj.Baz();
}
在这里,Foo(new Bar())
总是选择通用方法,无论 Bar
是 class 还是结构:
void Foo<T>(T obj)
where T : struct, IBar
=> obj.Baz();
void Foo(IBar obj)
=> obj?.Baz();
这让我眼睛流血:
void FooVal<T>(T obj)
where T : struct, IBar
=> obj.Baz();
void FooRef(IBar obj)
=> obj?.Baz();
那么对此有最佳实践吗?我愿意接受所有建议。
编辑:
该问题被标记为 Generic constraints, where T : struct and where T : class 重复,并且使用的是旧标题。所以我更新了标题以更好地传达我的问题。我要问的是,如何调用泛型方法并仅在参数不为 null 时才使用参数 ,无需装箱。
可以使用针对链接问题 解释的一些解决方法 来回答这个问题,但我仍然认为这是一个根本不同的问题。
我想出了这个帮手:
public static class Null
{
public static bool IsNull<T>(T obj) => !Data<T>.HasValue(obj);
private static class Data<T>
{
public static readonly Func<T, bool> HasValue = InitHasValue();
private static Func<T, bool> InitHasValue()
{
var type = typeof(T);
if (!type.IsValueType)
return obj => obj != null;
if (type.IsGenericType &&
type.GetGenericTypeDefinition() == typeof(Nullable<>))
{
var value = Expression.Parameter(type, "value");
var getter = type.GetProperty("HasValue").GetMethod;
var call = Expression.Call(value, getter);
var lambda = Expression.Lambda<Func<T, bool>>(call, value);
return lambda.Compile();
}
return obj => true;
}
}
}
所以我可以这样做:
void Foo<T>(T obj)
where T : IBar
{
if (!Null.IsNull(obj)) // Does not box.
obj.Baz();
}
刚遇到类似的问题,我想到了以下解决方案:
void Foo<T>(T obj)
{
if (obj is object)
obj.Baz();
}
如果 obj
为 null 或值为 null
的可空类型,则 is
操作的结果始终为 false
,如果不是,则检查是否存在从 T
到 object
的装箱转换,情况总是如此。
除非编译器根本没有优化,否则这实际上应该只是一个 null
检查,或者是对不可空类型的 noop。