【问题标题】:How to abstract operation from unrelated types?如何从不相关的类型中抽象出操作?
【发布时间】:2017-04-19 20:21:49
【问题描述】:

重新考虑我的问题后发表评论。 虽然@Dogu Arslan 提出了我使用WriteData 方法的确切示例的最佳解决方案,但问题标题的问题实际上是由@InBetween 和@Fabio 解决的。 IE。对于我使用 WriteData 方法的确切示例,最好将转换逻辑移出 WriteData 方法,但要从不相关的类型中抽象出逻辑,最好使用建议的重载。所以这三个答案对我都有帮助。

我已经阅读了以下类似问题 - 12 和其他问题 - 但在我的情况下没有找到合适的解决方案。

我的情况是:我有以下简单的方法。重要的部分是注释行和“来源”参数。其余部分并不那么重要,仅用于表明问题标题中存在“操作”。

void WriteData(
    int count, 
    int currentIndex, 
    List<byte> source, 
    StreamWriter sw, 
    string fileName)
{
    var countToWrite = count - currentIndex;

    if (countToWrite == 0)
        return;

    if (sw == null)
        sw = new StreamWriter(GetFullPath(fileName));

    //---------- Source's elements must be converted to string.
    var dataToWrite =
        source.GetRange(currentIndex, countToWrite)
        .Select(x => Convert.ToString(x));

    StringBuilder sb = new StringBuilder();

    foreach (var item in dataToWrite)
        sb.AppendLine(item);

    sw.Write(sb.ToString());
}

现在我希望“源”参数是包含字节、双精度或字符串的列表。我是否编写了三个 WriteData 方法的副本,只进行了一次更改 - “源”中的列表类型?还是有更好的方法?

我尝试了类型约束,但要约束什么类型?

我尝试检查类型,如果它不在我的类型列表(字节、双精度、字符串)中,则抛出异常。但是异常只在运行时起作用,我希望它在编译时起作用。

现在我不得不将运行时类型检查作为临时解决方案来限制自己,但是,正如我之前提到的,它不适合透视。

【问题讨论】:

  • 你能不能让它通用,并检查它是否以编程方式给出类型/

标签: c# type-constraints


【解决方案1】:

为什么不使用具有“隐藏”实际解决方案的重载方法

public void WriteData(List<byte> source) { WriteData<byte>(source); }
public void WriteData(List<double> source) { WriteData<double>(source); }
public void WriteData(List<string> source) { WriteData<string>(source); }

private void WriteData<T>(List<T> source)
{
     // Your actual implementation
}

在私有方法中,您可以检查类型并在给出错误类型时抛出异常,以防您希望“保护”不因“错误”而将私有方法公开。

【讨论】:

  • 抛出异常与运行时有关,但我想要一个编译时错误或警告。
  • @Alex34758,抛出异常只是一层“保护”。具有显式声明类型的公共重载为您提供编译时检查
  • 您的解决方案对我的确切问题有好处,但我同意@DoguArslan 必须重新考虑这个问题,最好从 WriteData 方法中删除转换逻辑(我将转换“ source”在调用 WriteData 方法之前使用 Convert 类,然后将结果作为类型为 List 的“source”参数传递,这样问题就可以消除。正确的架构是关键。
  • 将转换的责任转移到方法之外是对设计的正确重构。无论如何,所有三个答案都为您提供相同的解决方案 - 重载方法。构造函数与其他方法相同。
【解决方案2】:

最好的解决方案是公开公开所需的重载,然后将实现委托给私有泛型方法:

public void WriteData( , , List<string> source, , ) { WriteData<string>(...); }
public void WriteData( , , List<byte> source, , ) { WriteData<byte>(...); }
public void WriteData( , , List<double> source, , ) { WriteData<double>(...); }

private void WriteData<T>( , , List<T> source, , ) 
{ 
    Debug.Assert(typeof(T).Equals(typeof(string)) ||
                 typeof(T).Equals(typeof(byte)) ||
                 typeof(T).Equals(typeof(double)));
    ...
}

【讨论】:

  • 哦,你就像@Fabio 那样做。但是,如果公共 WriteData 方法中 List 元素的显式类型已经限制了泛型类型 T,为什么还要使用 Debug.Assert?看来 Debug.Assert 永远不会有“假”条件。
  • @Alex34758 因此Debug.Assert 而不是throw 任何例外。 outside 你的类不会调用无效类型(反射除外),但没有什么可以阻止来自你的类inside 的无效调用。断言作为安全网存在。
  • 您的解决方案对我的确切问题有好处,但我同意@DoguArslan 必须重新考虑这个问题,最好从 WriteData 方法中删除转换逻辑(我将转换“ source”在调用 WriteData 方法之前使用 Convert 类,然后将结果作为类型为 List 的“source”参数传递,这样问题就可以消除。正确的架构是关键。
【解决方案3】:

您可以做的一件事是将字符串转换责任完全转移到另一个类。即。 StringConverter 类将具有重载的构造函数,这些构造函数将采用这些不同的输入类型将它们转换为字符串并提供返回字符串的方法。这样,即使您将来扩展您支持的类型,您也不需要在示例中更改此方法,所有这些在您的字符串转换器类后面都是透明的。检查也将在编译时进行,因为字符串转换器类将通过其构造函数获取输入。是的,这意味着每次您想调用 WriteData 方法时,您都需要实例化一个新的 StringConverter 对象,但 C# 不提供您要求现成的级别的类型约束。

【讨论】:

  • 我同意你的看法。尽管 InBetween 和 Fabio 也提出了很好的解决方案,但我认为最好的方法是将转换逻辑移出 WriteData 方法。
猜你喜欢
  • 1970-01-01
  • 2015-12-16
  • 2011-11-19
  • 1970-01-01
  • 1970-01-01
  • 2015-06-13
  • 1970-01-01
  • 1970-01-01
  • 2014-06-27
相关资源
最近更新 更多