簡體   English   中英

C# 返回泛型抽象類

[英]C# Return generic abstract class

我有以下類型層次結構:

public interface IDocument<TItem>
{
    IEnumerable<TItem> Query(string query);

    string FileName { get; }
}

public abstract class Document<TDocument, TItem> : IDocument<TItem>
{
    public abstract IEnumerable<TItem> Query(string query);

    public string FileName { get; private set; }

    protected readonly TDocument _content;
}

public class DocumentX : Document<string, int>
{
    ...
}

public class DocumentY : Document<string, TypeOther>
{
    ...
}

等等...

我想創建一個工廠方法,如下所示:

private IEnumerable<IDocument<T>> Factory<T>()
where T : IDocument<T>
{
    yield return new DocumentX();
    yield return new DocumentY();
}

目標是擁有一個工廠方法,該方法將返回具有不同具體實現的集合,這些實現是從公共接口(IDocument)派生的

但是會引發編譯器錯誤:“無法將類型 'DocumentX' 隱式轉換為 'IDocument'。存在顯式轉換(您是否缺少強制轉換?)”

我錯過了什么?

這是對泛型的濫用。 如果你的方法是通用的,它不應該決定T是什么。 調用者決定T是什么。 現在,您的Factory方法正在假設T到底是什么。 這表明Factory不應該是泛型的。

你想說什么,就像

嘿, Factory調用者,我將返回一堆IDocument<T> ,但您不知道每個的T將是什么。

這就是將DocumentXDocumentY放入IEnumerable時會發生的情況。 想象一下,我正在使用Factory返回的IEnumerable 我不知道我得到的每個元素是什么類型的文檔。 IE

foreach (IDocument<???> document in Factory()) {
    ??? queryResult = document.Query("some query");
}

我不知道我應該放什么??? .

嗯,實際上,我對Query會返回什么有一點想法。 如果我寫:

foreach (IDocument<object> document in Factory()) {
    object queryResult = document.Query("some query");
}

那會起作用,因為一切都可以轉換為object 所以我們只需要編寫一個符合IDocument<object>AnyDocument類:

public class AnyDocument : IDocument<object> {
    public string FileName { get; private set; }
    private Func<string, IEnumerable<object>> query;

    public IEnumerable<object> Query(string query)
    {
        return this.query(query);
    }

    private AnyDocument() { }
    
    // this is used to convert an `IDocument<T>` to an AnyDocument
    public static AnyDocument FromDocument<T>(IDocument<T> document) {
        var doc = new AnyDocument();
        doc.FileName = document.FileName;
        doc.query = s => document.Query(s).Cast<object>();
        return doc;
    }
}

現在你可以像這樣聲明Factory

private IEnumerable<IDocument<object>> Factory()
{
    yield return AnyDocument.FromDocument(new DocumentX());
    yield return AnyDocument.FromDocument(new DocumentY());
}

這整個事情本來是簡單了很多,如果TIDocument<T>被限制為引用類型,即沒有IDocument<int>DocumentX 因為那樣你就可以使用通用方差:

// just add the word "out"
public interface IDocument<out TItem>

實際上,我會說無論如何你都應該這樣做,因為如果T是引用類型,你可以直接返回文檔對象,並且僅當T是值類型時才使用AnyDocument

private IEnumerable<IDocument<object>> Factory()
{
    yield return AnyDocument.FromDocument(new DocumentX());
    yield return new DocumentY(); // let's say "OtherType" is a reference type
}

嘗試:

private IEnumerable<IDocument<T>> Factory<T>() where T : IDocument<T>, class
{
    yield return new DocumentX() as T;
    yield return new DocumentY() as T;
}

通過將DocumentXDocumentYT您可以確保所有這些都至少是IDcoument<T>類型,如果不是實現IDocument<T>的類T

暫無
暫無

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

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