编译器给出隐式转换错误||我的泛型方法的约束是一个抽象泛型类

时间:2015-07-24 19:21:30

标签: c# generics inheritance

我正在编写一个程序并发现了一些常见的行为,所以我认为这对于抽象基类来说是一个合适的用例。

这是我的抽象基类的简化版:

    public abstract class BehaviorClass<T> where T: IDomainObj
    {
      protected BehaviorClass(var x){...}
      public abstract void Create(List<T> list, out string message);
      public abstract void Delete(List<T> list, out string message);
      ...
    }

这是派生类的简化版:

    public class DbSheets : BehaviorClass<Sheet>
    {
      public override void Create(List<Sheet> sheets, out string message){...}
      public override void Delete(List<Sheet> sheets, out string message){...}
      ...
    }

这是我想要对派生类进行操作的通用方法

    public void Import<DbObj>() where DbObj : BehaviorClass<IDomainObj>
    {
      var instance = (DbObj)Activator.CreateInstance(typeof(DbObj), DbAccessor);

      // STEP 1: Remove existing values
      var existingValues = instance.Read();
      instance.Delete(existingValues, out message);

      // STEP 2: Create new IDomainObjects 
      var domainObjects = //LINQ Query.ToList();

      // STEP 3: Add new IDomainObjects to the instance
      instance.Create(domainObjects, message);
    }

到目前为止,在我尝试调用导入方法之前,所有内容都可以正常编译。

    internal class Program
    {
      ...
      intermediary.Import<DbSheets>();
      ...
    }

这是尝试调用导入方法的结果错误

  

类型'namespace.DbSheets'不能在泛型类型或方法'intermediary.Import<DbObj>()'中用作类型参数'DbObj'。   从'namespace.DbSheets'到'namespace.BehaviorClass<IDomainObj>'没有隐式引用转换。

我的思维过程的

摘要: 本质上,我想要一个泛型方法只对从 BehaviorClass 派生的类进行操作,因为我可以可靠地知道它们共享一组常用的方法和属性。 Resharper说如果删除Import方法的泛型约束,代码将编译。我宁愿不删除该约束,因为此方法特别依赖于此共享行为将存在的事实。

注意: 我使用IDomainObj接口作为将泛型参数限制为特定类集的方法。它目前不包含任何特定功能。

2 个答案:

答案 0 :(得分:2)

在我看来,您需要两个通用类型参数:

public void Import<TBehavior, TDomain>()
    where TBehavior : BehaviorClass<TDomain>
    where TDomain : IDomainObj
{
    var instance = (TBehavior) Activator.CreateInstance(typeof(TBehavior), DbAccessor);

    // STEP 1: Remove existing values
    var existingValues = instance.Read();
    instance.Delete(existingValues, out message);

    // STEP 2: Create new IDomainObjects 
    var domainObjects = //LINQ Query.ToList();

    // STEP 3: Add new IDomainObjects to the instance
    instance.Create(domainObjects, message);
}

现在您应该可以致电:

Import<DbSheets, Sheet>();

之前的问题是,DbSheets 不是 BehaviorClass<IDomainObj> - 例如,您无法拨打sheets.Create(new List<IDomainObj>())

必须指定两个类型参数,这有点笨拙,并且可能有办法避免它,但我认为这是最简单的方法。

答案 1 :(得分:1)

由于Import函数似乎与Behavior类紧密耦合(几乎就像它应该封装在Behavior类中),为什么不这样做:

public abstract class BehaviorClass<TBehavior, TDomainObj>
    where TBehavior : BehaviorClass<TBehavior, TDomainObj> 
    where TDomainObj : IDomainObj
{
  protected BehaviorClass(var x){...}
  public abstract void Create(List<T> list, out string message);
  public abstract void Delete(List<T> list, out string message);
  ...
  public static void Import()
  {
    var instance = (TBehavior)Activator.CreateInstance(typeof(TBehavior), DbAccessor); // <- where did DbAccessor come from?

    // STEP 1: Remove existing values
    var existingValues = instance.Read();
    instance.Delete(existingValues, out message);

    // STEP 2: Create new IDomainObjects 
    var domainObjects = //LINQ Query.ToList();

    // STEP 3: Add new IDomainObjects to the instance
    instance.Create(domainObjects, message);
  }
}

像这样使用:

public class DbSheets : BehaviorClass<DbSheets, Sheet>
{
  public override void Create(List<Sheet> sheets, out string message){...}
  public override void Delete(List<Sheet> sheets, out string message){...}
  ...
}

internal class Program
{
  ...
  DbSheets.Import();
  ...
}

加成:

由于您无论如何都在硬编码DbAccessor(这是从哪里来的),请执行以下操作以避免代码中的Activator.CreateInstance(它可能仍然被底层框架使用,但这不是您的关注,框架团队可能会优化它稍后)。

public abstract class BehaviorClass<TBehavior, TDomainObj>
    where TBehavior : BehaviorClass<TBehavior, TDomainObj>, new() 
    where TDomainObj : IDomainObj
{
  protected BehaviorClass():this(DbAccessor){} // <- where did DbAccessor come from originally?
  protected BehaviorClass(var x){...}
  public abstract void Create(List<T> list, out string message);
  public abstract void Delete(List<T> list, out string message);
  ...
  public static void Import()
  {
    var instance = new TBehavior();

    // STEP 1: Remove existing values
    var existingValues = instance.Read();
    instance.Delete(existingValues, out message);

    // STEP 2: Create new IDomainObjects 
    var domainObjects = //LINQ Query.ToList();

    // STEP 3: Add new IDomainObjects to the instance
    instance.Create(domainObjects, message);
  }
}