IList <t>逆变和铸造问题

时间:2016-03-17 15:13:10

标签: c# list

我有一个FileRecord类,其定义如下:

public class FileRecord
{
    public IList<IDataRecord> DataRecords { get; set; }
    public IList<ImageFile> Images { get; set; }
    public string IndexFileName { get; set; }
    public string IndexFilePath { get; set; }
}

请注意,datarecords是接口类型的IList。

然后我有一个方法为我们拥有的每种类型的数据线创建一个文件记录:

fileRecord = new FileRecord
{
    DataRecords = CsvFile.Read<VallDataData>(Path.GetFullPath(indexFile.FullName)).Where(x => x.GiftAidValidity == "Y").ToList<IDataRecord>()
};

如果我删除ToList<IDataRecord>() to ToList()上的隐式转换,我会收到以下错误消息:

Error   CS0266  Cannot implicitly convert type 'System.Collections.Generic.List<VallDataData>' to 'System.Collections.Generic.IList<IDataRecord>'. An explicit conversion exists (are you missing a cast?)  

即使VallDataData实现了IDataRecord

为什么会这样?

感谢。

3 个答案:

答案 0 :(得分:4)

IList<IDataRecord>应该允许您向其添加任何 IDataRecord;您的列表只允许您添加VallDataData

如果您只需要从列表中读取,请尝试使用IReadOnlyList<IDataRecord>,因为它实际上是协变的。

答案 1 :(得分:3)

IList<T>既不是逆变量也不是协变量,因为它在方法中的输入和输出位置都有T。这就是你得到的原因:

  

错误CS0266无法隐式转换类型   &#39; System.Collections.Generic.List&#39;至   &#39; System.Collections.Generic.IList&#39 ;.一个明确的   存在转换(你错过了演员吗?)

您应该在这里使用CastToList<T>

如果IList<T>是协变或逆变的,那么它应该是inout之前的通用参数T(例如,参见IEnumerable<T>),但它事实并非如此。

答案 2 :(得分:1)

您无法将List<Derived>隐式转换为IList<Base>,因为IList<T>不是协变的(请参阅about covariance)。

IList<T>不是协变的,因为它是可变的(参见this SO question & answer

考虑以下,如果它是协变的

public interface INameable 
{
    string Name { get; }
}

public class Person : INameable
{
    public Person(string name)
    {
        Name = name;
    }

    public string Name { get; private set; }
}

public class Star : INameable
{
    public Star(string name, int brightness)
    {
         Name = name;
         Brightness = brightness;
    }

    public int Brightness { get; private set; }

    public string Name { get; private set; }

}

public void MyMethod() 
{
    List<Person> people = new List<Person> { new Person("Sam"), new Person("Pop") };

    // note: I *cannot* do the following, but let's pretend I can
    IList<INameable> nameables = people;

    // note: nameables and people technically reference the same object
    // so consider what happens when I do this:
    nameables.Add(new Star("Alpha Centauri", 9000));

    // nameables is cool, but oh man, my people list is now totally messed up. Sabotage!
    // Thanks goodness for covariance in generics!
}

总之,我会考虑尽可能使用IEnumerable<T>代替IList<T>。通常很好的做法是不要让你的对象变得可变(除非你真的想要它们)。

不变性的例子(来自OP的代码):

public class FileRecord
{
    public IEnumerable<IDataRecord> DataRecords { get; private set; }
    public IEnumeable<ImageFile> Images { get; private set; }
    public string IndexFileName { get; private set; }
    public string IndexFilePath { get; private set; }
}