【问题标题】:.NET 4.0 Generic Invariant, Covariant, Contravariant.NET 4.0 通用不变量、协变、逆变
【发布时间】:2011-02-14 03:52:49
【问题描述】:

这是我面临的情况:

public abstract class Record { } 

public abstract class TableRecord : Record { } 

public abstract class LookupTableRecord : TableRecord { } 

public sealed class UserRecord : LookupTableRecord { } 

public interface IDataAccessLayer<TRecord> 
    where TRecord : Record { } 

public interface ITableDataAccessLayer<TTableRecord> : IDataAccessLayer<TTableRecord> 
    where TTableRecord : TableRecord { } 

public interface ILookupTableDataAccessLayer<TLookupTableRecord> : ITableDataAccessLayer<TLookupTableRecord> 
    where TLookupTableRecord : LookupTableRecord { } 

public abstract class DataAccessLayer<TRecord> : IDataAccessLayer<TRecord> 
    where TRecord : Record, new() { } 

public abstract class TableDataAccessLayer<TTableRecord> : DataAccessLayer<TTableRecord>, ITableDataAccessLayer<TTableRecord> 
    where TTableRecord : TableRecord, new() { } 

public abstract class LookupTableDataAccessLayer<TLookupTableRecord> : TableDataAccessLayer<TLookupTableRecord>, ILookupTableDataAccessLayer<TLookupTableRecord> 
    where TLookupTableRecord : LookupTableRecord, new() { } 

public sealed class UserDataAccessLayer : LookupTableDataAccessLayer<UserRecord> { }

现在,当我尝试将 UserDataAccessLayer 转换为它的通用基类型 ITableDataAccessLayer&lt;TableRecord&gt; 时,编译器抱怨它无法隐式转换类型。

当我尝试在泛型参数的接口声明中使用 inout 关键字时,编译器会抱怨 Invalid variance: The type parameter must be invariantly valid. p>

我有以下抽象类:

public abstract class FileProcessor : IDisposable
{
    protected abstract ITableDataAccessLayer<TableRecord> CreateTableDataAccessLayer();
}

具体实现示例如下:

public class UserFileProcessor : FileProcessor
{
            protected override ITableDataAccessLayer<TableRecord> CreateTableDataAccessLayer()
        {
            return new UserDataAccessLayer();
        }
}

返回新的 UserDataAccessLayer();是编译器抱怨的地方。

【问题讨论】:

  • 请发布一个简短但完整的程序来展示问题,这样您也可以获得具体的答案。
  • 嗨 Lasse,我已根据您的建议对我的帖子进行了修改。任何帮助将不胜感激。
  • 我也想查看整个 UserDataAccessLayer 定义,所有方法,但请稍等,让我添加一个答案,然后您看看是否有帮助。

标签: .net generics .net-4.0 covariance contravariance


【解决方案1】:

据我所知,如果你想使用协方差,你需要为接口指定 out 关键字。

http://geekswithblogs.net/NewThingsILearned/archive/2009/09/30/covariance-in-.net-4.0.aspx

【讨论】:

  • 我也尝试在泛型接口上设置泛型参数。但是后来编译器给了我一个不同的错误:无效的方差:类型参数'TTableRecord'必须在...上始终有效。
  • 如果我将 out 关键字应用于通用 ITableDataAccessLayer 接口声明中的 TTableRecord 通用参数,我会收到以下错误:无效的差异:类型参数 'TTableRecord' 必须在 'Csss.Data 上逆变有效.DataAccessLayers.ITableDataAccessLayer.Delete(TTableRecord, bool)'。 'TTableRecord' 是协变的。
【解决方案2】:

协变和逆变的问题在于它对所涉及的类型施加了相当多的限制,因此它可能不适用于所有情况。

通过进行以下更改,我设法编译了您的代码:

public interface IDataAccessLayer<out TRecord>
    where TRecord : Record { }

public interface ITableDataAccessLayer<out TTableRecord> : IDataAccessLayer<TTableRecord>
    where TTableRecord : TableRecord { }

注意:

  • 为 IDataAccessLayer 和 ITableDataAccessLayer 添加了输出

然而,这意味着您不仅限于在这些类型的输出位置使用 TTableRecord,这意味着:

  • 只读属性的类型(不可写属性)
  • 方法的返回类型
  • 方法的参数类型

你可以将它用于:

  • 可写属性
  • 方法的引用或非输出/引用参数

很可能,这里没有办法让协方差和反方差对您有所帮助。

【讨论】:

  • 嗨 Lasse 不起作用。向 ITableDataAccessLayer 添加一个使用 TTableRecord 类型作为参数或返回类型的方法。
  • 我还能做些什么来将具体类型向下转换为基本泛型类型?
  • 正如我所说,它有很多限制,我怀疑你可以为所欲为。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-06
相关资源
最近更新 更多