下面是针对Nullable<T>
的扩展方法。
using System;
using System.Runtime.CompilerServices;
namespace DoNotationish
{
public static class NullableExtensions
{
public static U? Select<T, U>(this T? nullableValue, Func<T, U> f)
where T : struct
where U : struct
{
if (!nullableValue.HasValue) return null;
return f(nullableValue.Value);
}
public static V? SelectMany<T, U, V>(this T? nullableValue, Func<T, U?> bind, Func<T, U, V> f)
where T : struct
where U : struct
where V : struct
{
if (!nullableValue.HasValue) return null;
T value = nullableValue.Value;
U? bindValue = bind(value);
if (!bindValue.HasValue) return null;
return f(value, bindValue.Value);
}
}
}
这使得Nullable<T>
可以在查询语法中使用。下面的测试将通过。
[Test]
public void Test1()
{
int? nv1 = 5;
int? nv2 = 3;
var q = from v1 in nv1
from v2 in nv2
select v1 + v2;
Assert.AreEqual(8, q);
}
[Test]
public void Test2()
{
int? nv1 = null;
int? nv2 = 3;
var q = from v1 in nv1
from v2 in nv2
select v1 + v2;
Assert.IsNull(q);
}
不过,如果你尝试链式调用三个或更多的方法,它将被视为匿名类型并且无法编译。
[Test]
public void Test3()
{
int? nv1 = 5;
int? nv2 = 3;
int? nv3 = 8;
var q = from v1 in nv1
from v2 in nv2 // Error CS0453: anonymous type is not struct
from v3 in nv3
select v1 + v2 + v3;
Assert.AreEqual(16, q);
}
您可以通过手动指定使用
ValueTuple
来解决此问题,但这样做不太美观。 [Test]
public void Test3_()
{
int? nv1 = 5;
int? nv2 = 3;
int? nv3 = 8;
var q = from v1 in nv1
from v2 in nv2
select (v1, v2) into temp // ugly
from v3 in nv3
select temp.v1 + temp.v2 + v3; // ugly
Assert.AreEqual(16, q);
}
这些简化示例可以通过使用 +
运算符简单解决:var q = nv1 + nv2 + nv3;
然而,如果您希望编写流畅的代码,更方便地使用用户定义的结构体是否有好的方法呢?