C#中的通用空对象模式
我想知道是否有任何方法在C#中实现通用的空对象模式。 genericsnull对象是所有引用类型的子类,就像Scala中的Nothing
一样。 这好像是
public class Nothing : T where T : class
但它无法编译,我不知道如何实现T
的方法来提供默认行为或抛出exception 。 以下是一些想法:
- 用reflection?
- 创建
Nothing
时使用表达式树? 它可能看起来像Moq。 另一个问题是:在产品代码中使用模拟框架/库是否可以? - 使用动态类型?
我知道也许我应该为特定类型实现特定的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