簡體   English   中英

空泛型類型 c#

[英]Null generic type c#

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

這不起作用,因為在 C# 中所有類型都不能為 null。 令我驚訝的是,當我在 Java 中嘗試類似的事情時,它起作用了! 我在 Stack Overflow 上看到了一些相關的問題,但他們沒有給出我想要的結果。 我不想使用default(E)

您需要一個泛型類型約束,聲明E必須是引用類型:

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

也就是說,除非出於其他原因還需要E成為值類型。 如果是這種情況,您需要犧牲一個或另一個要求。

可空值類型是一種選擇:對於您的原始版本,缺少類型約束(因為Nullable<T>本身就是值類型),您可以使用int? . 以下代碼在沒有約束的情況下為我編譯:

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

int? 不是int ,但也不完全不是int ,是嗎?

令我驚訝的是,當我在 Java 中嘗試類似的東西時,它確實有效。

這是因為 Java 泛型類型是通過類型擦除實現的,這實際上意味着它們都是java.lang.Object后代。

例如,您不能在 Java 中使用原始int作為Node的類型參數:您被迫使用java.lang.Integer代替。 因此,無論T是什么, element都可以被賦值為null

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

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

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

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

如果我們這樣做怎么辦:

首先創建一個簡單的界面

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM