泛型 C# 中的逆变

Contravariance in Generics C#

不知道谁解析了这个有差异的段码:

我有一个抽象的爸爸class:

public abstract class PdfObject
{...}

还有两个child classes:

public class PdfText : PdfObject
{...}

public class PdfImage : PdfObject
{...}

现在,我的错误或经验代码是下一个:

public IList<PdfText> GetTexts()
{
       List<PdfText> result = new List<PdfText>();
       List<PdfObject> list = GetList();
       foreach(var item in list)
       {
           if(item is PdfText) result.Add(item)
       }
       return result;
} 

public List<PdfObject> GetList()
{...}

好吧,我读了很多这个主题,但不支持如何在泛型中使用变体或使用更好的解决方案来解决这个问题。

请帮助我,谢谢。

你可以这样做...

public IList<PdfText> GetTexts()
{
    List<PdfText> result = GetList()
        .Where(x => x is PdfText)
        .Select(x => (PdfText)x)
        .ToList();
    return result;
}

已编辑:这行得通,但 OfType 更好。

这与方差没有直接关系。您的问题在这里:

public IList<PdfText> GetTexts()
{
    List<PdfText> result = new List<PdfText>();
    List<PdfObject> list = GetList();
    foreach(var item in list)
    {
        if(item is PdfText) result.Add(item)
    }
    return result;
} 

item变量的静态类型是PdfObject所以不能加到result;你需要施放它。例如

if (item is PdfText) result.Add((PdfText)item);

这是低效的,因为您检查了两次类型:一次用于 is 运算符,一次用于强制转换。相反,您应该这样做:

public IList<PdfText> GetTexts()
{
    List<PdfText> result = new List<PdfText>();
    List<PdfObject> list = GetList();
    foreach(var item in list)
    {
        var textItem = item as PdfText
        if (textItem != null) result.Add(textItem)
    }
    return result;
} 

或者,您可以使用 linq:

var result = GetList().OfType<PdfText>().ToList();

在这种情况下,您可能有更好的解决方案。

public class ClientOfPdfObject<T> where T: PdfObject
{
    public List<T> GetItems()
    {
        List<PdfObject> list = GetList();
        var result = new List<T>();
        foreach (var pdfObject in list)
        {
            if (typeof (T) == pdfObject.GetType())
                result.Add((T) pdfObject);
        }
        return result;
    }

    //Get PdfObjects somewhere (ex. Db)
    private List<PdfObject> GetList()
    {
        var list = new List<PdfObject>
        {
            new PdfImage(),
            new PdfImage(),
            new PdfImage(),
            new PdfText(),
            new PdfText(),
            new PdfText(),
            new PdfText()
        };

        return list;
    }
}


static void main()
{
        var text = new ClientOfPdfObject<PdfText>();
        //contains 4 itmes (PdfText)
        var pdfTexts = text.GetItems();

        var image = new ClientOfPdfObject<PdfImage>();
        //contains 3 items (PdfImage)
        var pdfImages = image.GetItems();
}

明天,当您添加更多 pdf 对象(例如 PdfGraph)时,您不需要更改任何内容。