在C#泛型空对象模式(Generic null object pattern in C#)

2019-09-19 03:33发布

我不知道是否有实现在C#泛型空对象模式的任何做法。 通用null对象是所有的引用类型的子类,就像Nothing在斯卡拉。 这好像是

public class Nothing<T> : T where T : class

但它不能编译,我不知道如何实现的方法T提供默认的行为或抛出异常 。 下面是一些思路:

  1. 使用反射?
  2. 在创建时使用表达式树Nothing<T> 它也许看起来像起订量。 而另一个问题来了:那是正确的产品代码使用模拟框架/库?
  3. 使用动态类型?

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

任何建议? 谢谢。

Answer 1:

有了泛型,你不能从定义继承T 。 如果您的目的是使用if(x is Nothing<Foo>)那么,仅仅是行不通的。 并非最不重要的,你需要考虑抽象类型,密封型和非默认构造。 但是,你可以这样做:

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

然而,IMO失败大部分的空对象的主要特征; 特别是,你可以很容易地结束与某人做的:

Nothing<Foo>.Instance.SomeProp = "abc";

(也许意外,传递对象3级的水平向下之后)

坦率地说,我觉得你应该只检查null



Answer 2:

这个怎么样?

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


Answer 3:

由于是密封类,您不能在普通的情况下,这样的继承。 预计不会很多类从派生,所以它可能不是好主意,如果这是可行的。

使用由@abatishchev建议隐含操作听起来像一个可能的方法。



Answer 4:

我用在我的项目是这样的:

public interface IOptional<T> : IEnumerable<T> { }
public interface IMandatory<T> : IEnumerable<T> { }

自IEnumerable衍生与LINQ兼容性两个接口

public class Some<T> : IOptional<T>
{
    private readonly IEnumerable<T> _element;
    public Some(T element)
        : this(new T[1] { element })
    {

    }
    public Some()
        : this(new T[0])
    {}
    private Some(T[] element)
    {
        _element = element;
    }
    public IEnumerator<T> GetEnumerator()
    {
        return _element.GetEnumerator();
    }
    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

public class Just<T> : IMandatory<T>
{
    private readonly T _element;

    public Just(T element)
    {
        _element = element;
    }
    public IEnumerator<T> GetEnumerator()
    {
        yield return _element;
    }
    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

类的实现只是和一些

注意:这个班的实施是非常相似的,但它有一个性差异。 类从接口IMandatory刚刚得到的,只有一个构造函数,它保证类的该实例就总是有内部的值。

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

一些扩展实用性

注意:扩展方法匹配需要两个函数,返回IMandatory,在此之后,扩展方法折使用。首先()没有任何检查。

现在我们可以使用LINQ的全功率和编写代码类似这一个(我的意思是单子.SelectMany())

var five = new Just<int>(5);
var @null = new Some<int>();

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


Answer 5:

如何在.NET Framework中已经存在的执行可空吗? http://msdn.microsoft.com/en-us/library/b3h38hb0.aspx



文章来源: Generic null object pattern in C#