【问题标题】:Compiler gives implicit conversion error || My generic method's constraint is an abstract generic class编译器给出隐式转换错误 ||我的泛型方法的约束是抽象泛型类
【发布时间】:2015-07-24 19:21:29
【问题描述】:

我正在编写一个程序并发现了一些常见行为,因此我认为这将是抽象基类的合适用例。

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

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

这是尝试调用 Import 方法的Resulting Error

类型“namespace.DbSheets”不能用作泛型类型或方法'intermediary.Import&lt;DbObj&gt;()' 中的类型参数“DbObj”。 没有从 'namespace.DbSheets' 到 'namespace.BehaviorClass&lt;IDomainObj&gt;' 的隐式引用转换。

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

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

【问题讨论】:

  • 简单地说,DbSheets不是BehaviorClass&lt;IDomainObj&gt;。您不能将 List&lt;IDomainObj&gt; 传递到其 Create 方法中。所以即使没有约束,你的演员也会失败。但是,如果不知道您要使用 instance 做什么,很难知道该建议什么。
  • 旁注:我已经从您的帖子中删除了很多“新此处/阅读此/谢谢”文本,因为它与问题没有直接关系。很明显,您在帖子中投入了大量工作,并展示了非常清晰的示例,使其成为一篇好帖子。
  • @JonSkeet 我在Import 方法中添加了我想对instance 对象执行的操作。

标签: c# generics inheritance


【解决方案1】:

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

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&lt;IDomainObj&gt; - 例如,您不能调用 sheets.Create(new List&lt;IDomainObj&gt;())

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

【讨论】:

    【解决方案2】:

    由于 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);
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-02-13
      • 1970-01-01
      • 2013-08-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-21
      • 2023-03-10
      相关资源
      最近更新 更多