空通用类型c#

时间:2017-06-29 18:00:19

标签: c# generics null

public class Node<E> : IPosition<E>
{
    private E element;
    public Node<E> PrevNode { get; set; }
    public Node<E> NextNode { get; set; }

    //Constructor
    public Node(E e, Node<E> p, Node<E> n)
    {
        element = e;
        PrevNode = p;
        NextNode = n;
    }
}

当我创建一个新的节点对象时,我有我想要的上述Node类, 能够做到这一点:

Node<E> n = new Node<E>(null, null, null);

这不起作用,因为所有类型都不能为空。让我感到惊讶的是,当我在Java中尝试类似的东西时,它是有效的。我在Stack Overflow上看到了一些相关的问题,但是他们没有给出我想要的结果。我不想使用default(E)

3 个答案:

答案 0 :(得分:9)

您需要一个泛型类型约束,指出E必须是引用类型:

public class Node<E> : IPosition<E> where E : class

也就是说,除非您因其他原因需要E作为值类型。如果是这种情况,则需要牺牲一项要求或另一项要求。

Nullable值类型是一个选项:对于原始版本,缺少类型约束(因为Nullable<T>本身就是值类型),您可以使用int?。以下代码为我编译而没有约束:

var y = new Node<int?>(null, null, null);

int?不是int,但它并非int,不是吗?

答案 1 :(得分:2)

  

令我惊讶的是,当我在Java中尝试类似的东西时,它可以工作。

这是因为Java泛型类型是使用类型擦除实现的,这实际上意味着它们都是java.lang.Object后代。

例如,您不能在Java中使用原始int作为Node的类型参数:您将被迫使用java.lang.Integer。因此,无论element如何,都可以为null分配T

在C#中,类型参数没有这样的限制:写Node<int>是完全合法的。但是,如果element类型为int,则您无法再编写element = null,这是您看到错误的根本原因。

除了您提到的default(T)方法之外,您还可以要求T是引用类型,如下所示:

public class Node<E> : IPosition<E> where E : class {
    ...
}

现在将null传递给Node构造函数的初始参数是合法的,但使用任何值类型实例化Node<T>都是合法的,包括{{ 1}}。

答案 2 :(得分:1)

如果我们这样做会怎么样:

首先创建一个简单的界面

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

并编写它的实现

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

在此之后,我们在您的Node类中进行一些更改

public class Node<E> : IPosition<E>
{
    private IOptional<E> element;
    public Node<E> PrevNode { get; set; }
    public Node<E> NextNode { get; set; }

    //Constructor
    public Node(IOptional<E> e, Node<E> p, Node<E> n)
    {
        element = e;
        PrevNode = p;
        NextNode = n;
    }
}

并使用它

Node<E> n = new Node<E>(
                new Maybe<E>(), 
                null, 
                null
             );
  

此字段上的Node类内不再有空检查

而不是这个

if (this.element != null) { .. }

像这样写

this.element.Select(e => { doSomething(e); return true; })
像这样

if (this.element.Any()) 
{ 
    var elem = this.element.First();
    // do something
}

或写一个小的扩展方法

public static IOptional<TOutput> Match<TInput, TOutput>(
    this IEnumerable<TInput> maybe,
    Func<TInput, TOutput> some, Func<TOutput> nothing)
{
    if (maybe.Any())
    {
        return new Maybe<TOutput>(
                    some(
                        maybe.First()
                    )
                );
    }
    else
    {
        return new Maybe<TOutput>(
                    nothing()
                );
    }
}

并且喜欢这个

var result = this.element
                 .Match(
                     some: e => e.ToString(),
                     nothing: () => "Ups"
                 )
                 .First();