C#中的通用空对象模式

我想知道是否有任何方法在C#中实现通用的空对象模式。 genericsnull对象是所有引用类型的子类,就像Scala中的Nothing一样。 这好像是

 public class Nothing : T where T : class 

但它无法编译,我不知道如何实现T的方法来提供默认行为或抛出exception 。 以下是一些想法:

  1. 用reflection?
  2. 创建Nothing时使用表达式树? 它可能看起来像Moq。 另一个问题是:在产品代码中使用模拟框架/库是否可以?
  3. 使用动态类型?

我知道也许我应该为特定类型实现特定的null对象。 我只是想知道是否有任何解决方案。

有什么建议吗? 谢谢。

使用generics,您无法从T定义inheritance。 如果你的意图是使用if(x is Nothing) ,那那就if(x is Nothing) 。 尤其是,您需要考虑抽象类型,密封类型和非默认构造函数。 但是,您可以执行以下操作:

 public class Nothing where T : class, new() { public static readonly T Instance = new T(); } 

但是,IMO失败了null对象的大部分关键特性; 特别是,你可能很容易就会有人做:

 Nothing.Instance.SomeProp = "abc"; 

(也许是在将物体向下传递3级后意外)

坦白说,我认为你应该检查是否为null

这个怎么样?

 public class Nothing where T : class { public static implicit operator T(Nothing nothing) { // your logic here } } 

由于存在密封类,因此无法在通用情况下进行此类inheritance。 预计很多课程都不会派生出来,因此如果能够奏效可能不是一个好主意。

使用@abatishchev建议的隐式运算符听起来像是一种可能的方法。

我在我的项目中使用这样的东西:

 public interface IOptional : IEnumerable { } public interface IMandatory : IEnumerable { } 

两个接口派生自IEnumerable以与LINQ兼容

 public class Some : IOptional { private readonly IEnumerable _element; public Some(T element) : this(new T[1] { element }) { } public Some() : this(new T[0]) {} private Some(T[] element) { _element = element; } public IEnumerator GetEnumerator() { return _element.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } public class Just : IMandatory { private readonly T _element; public Just(T element) { _element = element; } public IEnumerator GetEnumerator() { yield return _element; } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } 

类和Just的实现

注意:这个类的实现非常相似,但它有一个不同之处。 类刚刚从接口IMandatory派生而且只有一个构造函数,它保证了类的实例总是在里面有一个值。

 public static class LinqExtensions { public static IMandatory Match( this IEnumerable maybe, Func some, Func nothing) { if (maybe.Any()) { return new Just( some( maybe.First() ) ); } else { return new Just( nothing() ); } } public static T Fold(this IMandatory maybe) { return maybe.First(); } } 

实用性的一些扩展

注意:扩展方法匹配需要两个函数并返回IMandatory,之后,扩展方法Fold使用.First()而不进行任何检查。

现在我们可以使用LINQ的全部function并编写与此类似的代码(我的意思是monads .SelectMany())

 var five = new Just(5); var @null = new Some(); Console.WriteLine( five .SelectMany(f => @null.Select(n => f * n)) .Match( some: r => $"Result: {r}", nothing: () => "Ups" ) .Fold() ); 

在.NET Framework中已经存在的Nullable实现怎么样? http://msdn.microsoft.com/en-us/library/b3h38hb0.aspx