【问题标题】:C# overload a method with class specialisation as argumentC# 以类特化作为参数重载方法
【发布时间】:2020-07-02 17:58:29
【问题描述】:

我正在努力解决 C# 中的方法重载问题。

我有几个类继承自一个通用类

// I can not touch the implementation of these classes
class GenericDataType {}
class ProductDataType : GenericDataType {}
class ContentDataType : GenericDataType {}

还有一个类,它有一个迭代这些类列表的方法。 该方法的行为应该根据循环当前消耗的项目类型而有所不同

class DataTypeParser {
   public List<ParserdType> parseAll(List<GenericDataType> list) {
      var resultList = new List<ParserdType>();
      foreach(var item in list) {
          // if item is a ProductDataType do something
          // if item is a ContentDataType do something else
          // else do some generic stuff...
          // then add to resultList the current processed item
      }
   }
}

我认为最自然的方法是在GenericDataType中添加一个虚方法,然后在子类中添加该方法的实现,但是除了DataTypeParser之外,我无法触及任何类。

我不想在代码中添加一堆 if else,我宁愿使用一些多态的东西来轻松处理该 GenericDataType 的不同实现。

所以我的解决方案是在 DataTypeParser 中添加一些重载方法

class DataTypeParser {
   public List<ParserdType> parseAll(List<GenericDataType> list) {
      var resultList = new List<ParserdType>();
      foreach(var item in list) {
          var parsed = parse(item);
          if(parsed != null){
              resultList.Add(parsed);
          }
      }
   }
   public ParserdType parse(ProductDataType o){
      // do something
      // ...
      return new ParserdTypeProduct();
   }
   public ParserdType parse(ContentDataType o){
      // do something else
      // ...
      return new ParserdTypeContent();
   }
   public ParserdType parse(GenericDataType o){
      return null;
   }
}

但它不起作用,因为 C# 似乎只调用具有泛型参数的函数。

我错过了什么?

我是个菜鸟,尤其是对于 C#,所以我很容易错过一些重要的东西,即使是一些链接也可能很有帮助。

here is the example on dotnetfiddle

【问题讨论】:

  • 您正在寻找“双重调度”,这可能应该与stackoverflow.com/a/45053773/477420 重复,但我不知道是否可以建立这种联系。
  • do somethingdo something else 有何不同?它们是否处理 GenericDataType 或其子类的任何属性?
  • 据我所知,显而易见的选择是@AlexeiLevenkov 提到的“双重调度”使用反射,或者实现一个可以使用foreach(var type in IDataType){ type.parse(); }的接口
  • @Chetan Ranpariya 解析方法应该在DataTypes 和打印ParserdTypes 的视图之间建立一座桥梁
  • C# 解决方案(如链接所示)是 var parsed = parse((dynamic)item); 或者如果您不喜欢它的成本/魔力,John Wu 建议是非常标准的方法。

标签: c#


【解决方案1】:

多态是运行时的,而方法重载的解决是编译时的,所以你目前的方法当然行不通。

如果你想做“多态的东西”,你需要某种形式的虚拟方法表,你不能使用内置在你所拥有的类中的方法表,因为你不能修改它们来添加你需要的方法。

相反,您可以创建另一个表,例如

var methodTable = new Dictionary<Type,Action<GenericDataType>>
{
    { typeof(GenericDataType), x => DoParse(x) },
    { typeof(ProductDataType), x => DoParse(x as ProductDataType) },
    { typeof(ContentDataType), x => DoParse(x as ContentDataType) }
};

然后遍历列表并执行:

foreach (var item in list)
{
    methodTable[item.GetType()](item);
}

因为类型是在 lambda 表达式中强制转换的,所以现在将在运行时选择适当的重载。

【讨论】:

    【解决方案2】:

    最好的解决方案可能是使用可以被 ConcreteDataTypes 覆盖的函数来扩展 GenericDataType。

    class GenericDataType {
      public virtual ParsedType DoParse()
      {
        // e.g. standard stuff that can be done
      }
    }
    class ProductDataType : GenericDataType {
      public override ParsedType DoParse()
      {
        // Special stuff in ProductDataType
      }
    }
    class ContentDataType : GenericDataType {
      public override ParsedType DoParse()
      {
        // Special stuff in ContentDataType
      }
    }
    

    所以会调用正确的函数

    class DataTypeParser {
       public List<ParserdType> parseAll(List<GenericDataType> datatypes) {
          var resultList = new List<ParserdType>();
    
          foreach(var item in datatypes) {
            resultList.Add(item.DoParse());
          }
    
          return resultList;
       }
    }
    

    这也可以通过接口实现。如果您无法更改基类,您可以构建第二个类,该类继承自基类并执行 DoParse() 函数。

    class GenericParseDataType : GenericDataType {
      public virtual ParsedType DoParse()
      {
        // e.g. standard stuff that can be done
      }
    }
    class ProductDataType : GenericParseDataType {
      public override ParsedType DoParse()
      {
        // Special stuff in ProductDataType
      }
    }
    class ContentDataType : GenericParseDataType {
      public override ParsedType DoParse()
      {
        // Special stuff in ContentDataType
      }
    }
    

    【讨论】:

    • 这是显而易见的答案,但 OP 说他们不能碰这些课程。所以添加虚拟方法或接口似乎是不可能的。
    • 不幸的是,GenericDataType、ProductDataType 和 ContentDataType 不是我的东西,我无法触摸它们,我必须在 parseAll 中将它们作为参数来获取。我能接触到的所有东西都是 DataTypeParser 实现
    猜你喜欢
    • 2014-08-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多