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)
。
答案 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();