2016-03-02 38 views
0

我遇到意外问题。C# - 将变体类型的子类转换为父类

我想用特型演员子类,以他的父类与通用类型,但是编译器不希望这种事情发生......

这里是我的父类:

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类型是明确至极显然是实现System.Object

Edit

在我的情况下,co-variant接口不起作用。
这是我修改后的代码:

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

但投XlsxDataSourceIDataSource对象造成的InvalidCastException

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

这是因为它不是'DataSource ',它是'DataSource '。如果你想要共变,你将不得不使用接口而不是类。 – juharr

+0

但我在我的'DataSource'类中有一个通用构造函数,所以没有多余的代码,这对于一个接口是不可能的。 –

+1

你仍然可以在接口和子类之间有一个抽象类。只有在使用接口引用时,协变才有效。 https://msdn.microsoft.com/en-us/library/dd997386.aspx – juharr

回答

3

你需要一个联合变型接口才能工作

public interface IDataSource<out T> {} 

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

public class XlsxDataSource : DataSource<Row> {} 

W生病允许以下

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比的派生更少。

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

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,这将导致一个转换异常。所以最终一个更好的问题是为什么字典需要降低泛型类型的显然不是共变的。

+0

感谢这段代码,我一直在寻找如何实现联合变体界面:) –

+0

我试过你的解决方案,但它没有奏效,你能检查我的编辑吗? –

+0

@Renlidacha我根据你的修改更新了我的答案。但基本上你所尝试的都不起作用,因为它不是两种泛型的共同变体。 – juharr

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; 
+2

这是反变形。你需要使用'out'作为协方差。 – juharr

+0

@juharr:对,我的错。 –