C# - 将子类转换为具有变量类型的父类

时间:2016-03-02 15:57:56

标签: c# casting

我遇到了意想不到的问题。

我想将具有特殊类型的子类强制转换为具有泛型类型的父类,但编译器不希望这种情况发生......

这是我的父类:

public abstract class DataSource<T>
{
    // Abstract methods and constructor
}

其中一个子课程:

public class XlsxDataSource : DataSource<Row>
{
    // Overriden methods and constructor
}

代码给出错误:

XlsxDataSource dataSource = new XlsxDataSource();

var dataSources = new Dictionary<long, DataSource<Object>>();

dataSources[dataSource.id] = (DataSource<Object>) dataSource;

正如您所看到的,无论子类型如何,我都有一个Dictionary可以包含多个DataSources
但最后一行是给出了下一个编译错误:

Cannot convert type 'EDS.Models.XlsxDataSource' to 'EDS.Models.DataSource<object>'

我不明白为什么,因为XlsxDataSource是来自DataSource的孩子而且XlsxDataSource中的类型明确是Row,显然正在实施System.Object }。

  

修改

在我的情况下,共变体界面不起作用。
这是我修改过的代码:

public interface IDataSource<T, in T1>
{
    List<T> GetAllRows();

    List<Object> GetValues(T1 row); 
}

抽象类:

public abstract class DataSource<T, T1> : IDataSource<T, T1>
{
    public abstract List<T> GetAllRows();

    public abstract List<Object> GetValues(T1 row); 
}

最后是较少派生的类:

public class XlsxDataSource : DataSource<Row, Row>, IDataSource<Row, Row>
{
    public abstract List<Row> GetAllRows();

    public abstract List<Object> GetValues(Row row); 
}

这是演员:

IDataSource<Object, Object> datasource = (IDataSource<Object, Object>) new XlsxDataSource();

但是将XlsxDataSource投射到IDataSource对象会产生InvalidCastException

Unable to cast object of type 'EDS.Models.XlsxDataSource' to type 'EDS.Models.IDataSource`2[System.Object,System.Object]'.

2 个答案:

答案 0 :(得分:3)

您需要一个共变式界面才能实现此目的

public interface IDataSource<out T> {}

public abstract class DataSource<T> : IDataSource<T> {}

public class XlsxDataSource : DataSource<Row> {}

将允许以下

XlsxDataSource dataSource = new XlsxDataSource();
var dataSources = new Dictionary<long, IDataSource<Object>>();
dataSources[dataSource.id] = (IDataSource<Object>) dataSource;

但请注意,如果类型T仅用作方法的返回类型或仅用于只读属性(即它仅来自接口且永远不会插入),则应仅使用协方差。 )。

修改

根据您的修改,您现在有两种通用类型TT1。您已将T1定义为具有in的反变量,这对于您使用该类型的方式是正确的,但是反差方法仅允许您分配更多派生类型而不是更少派生类型。此外,您没有将T设置为变体,但如果将返回类型更改为IEnumerable<T>,则可能是共变体。这是你能做的。

public interface IDataSource<out T, in T1>
{
    IEnumerable<T> GetAllRows();

    List<Object> GetValues(T1 row); 
}

public abstract class DataSource<T, T1> : IDataSource<T, T1>
{
    public abstract IEnumerable<T> GetAllRows();

    public abstract List<Object> GetValues(T1 row); 
}

public class XlsxDataSource : DataSource<Row, Row>, IDataSource<Row, Row>
{
    public abstract IEnumerable<Row> GetAllRows();

    public abstract List<Object> GetValues(Row row); 
}

public class DerivedRow : Row {}

这将允许你做类似

的事情
XlsxDataSource dataSource = new XlsxDataSource();
IDataSource<object, DerivedRow> interfaceReference = dataSource;

您可以将其分配为允许更多派生类型的T1,因为您将它们传递到界面中。因此,方法GetValues(Row row)可以采用DerivedRow,但不能采用object。然后,对于T1,您可以分配较少的派生类型,因为您将从界面中获取这些值。因此,方法GetAllRows会返回IEnumerable<Row>,该IEnumerable<object>可分配给IEnumerable,因为object是协变的,Row的派生程度低于public interface IDataSource { List<object> GetAllRows(); List<object> GetValues(object row); } public abstract class DataSource<T> : IDataSource { public abstract List<T> GetAllRows(); List<object> IDataSource.GetValues(object row) { return GetValues((T)row); } public abstract List<object> GetValues(T row); List<object> IDataSource.GetAllRows() { return GetAllRows().Cast<object>().ToList(); } } public class XlsxDataSource : DataSource<Row> { public override List<object> GetValues(Row row) { throw new NotImplementedException(); } public override List<Row> GetAllRows() { throw new NotImplementedException(); } }

现在你可以做的是创建一个非通用的接口来处理这个问题。

XlsxDataSource dataSource = new XlsxDataSource();
var dataSources = new Dictionary<long, IDataSource>();
dataSources[5] = dataSource;

这将允许你做

GetValues

但是现在你已经失去了强类型,这意味着你不知道接口方法返回的列表中对象的实际类型,更糟糕的是你可以将对象传递给{{ 1}}将导致转换异常。因此,最终一个更好的问题是,为什么字典需要将泛型类型降级为明显不同变量的东西。

答案 1 :(得分:1)

发生了这个错误,因为C#中的泛型类不是协变的。

协方差使您可以使用比最初指定的派生类型更多的派生类型。

在C#协方差中可以在接口中使用,因此如果您可以将DataSource<T>类转换为接口,则可能的解决方案是:

public interface IDataSource<out T>
{
    ...
}

public abstract class DataSource<T> : IDataSource<T>
{
    ...
}

public class XlsxDataSource : DataSource<Row>
{
    // Overriden methods and constructor
}

并使用这样的词典:

XlsxDataSource dataSource = new XlsxDataSource();
var dataSources = new Dictionary<long, IDataSource<Object>>();
dataSources[dataSource.id] = (IDataSource<Object>)dataSource;