2017-06-29 159 views
1
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類,我想,當我創建一個新的節點對象, 才能夠做到這一點:空泛型類型C#

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

這並不是因爲所有類型的工作不能爲空。我驚訝的是,當我在Java中嘗試類似的東西時,它可以工作。我已經看到了堆棧溢出的一些相關問題,但他們沒有給出我想要的結果。我不想使用default(E)

+0

C#允許泛型與原始類型一起使用,而Java只允許引用類型與泛型。所有的引用類型都可以爲null,所以它可以在Java中使用。 – 4castle

+1

'Node n = new Node(null,null,null);'你必須提供特定類型才能工作。 – dcg

+2

'我不需要默認(E)'你不是嗎?那怎麼不*完全*你想要什麼? – Servy

回答

6

你需要一個泛型類型約束指出E必須是引用類型:

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

也就是說,除非你還需要E是出於其他原因的值類型。如果是這樣的話,你需要犧牲一個要求或另一個要求。

可爲空值類型是一個選項:使用原始版本,缺少類型約束(因爲Nullable<T>本身就是一個值類型),您可以使用int?。下面的代碼編譯,我沒有約束:

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

int?int,但已經很接近了。

2

我驚訝的是當我在Java中嘗試類似的東西時,它的工作原理。

這是因爲Java的泛型類型與類型擦除,這實際上意味着,他們都是java.lang.Object後代實現。

例如,您不能使用原始文件int作爲您的Node的Java類型參數:您不得不使用java.lang.Integer來代替。因此,無論T如何,element都可以被分配null

在C#中,類型參數沒有這樣的限制:編寫Node<int>是完全合法的。但是,element類型爲int您不能再編寫element = null,這是您看到的錯誤的根源。

除了default(T)辦法,你提到,你可以要求T是引用類型,像這樣:

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

現在是合法的傳遞nullNode的構造函數的初始參數,但用任何值類型實例化Node<T>都是非法的,包括Nullable<Tx>

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(); 
    } 
} 

此之後,我們做了一些變化,您的節點

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類裏面

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

沒有更多null檢查在這一領域

而是這個

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(); 
+0

看我的答案[關於null對象模式](https://stackoverflow.com/questions/11339729/generic-null-object-pattern-in-c-sharp/44789725#44789725) – kogoia

+0

我也建議刪除所有null在你的代碼,尤其是 作爲構造函數或方法中的參數 – kogoia