【问题标题】:Is there a way I can refactor this Generic class to avoid where constraints all over my C# code?有没有办法可以重构这个通用类来避免我的 C# 代码中的约束?
【发布时间】:2021-07-15 18:52:09
【问题描述】:

我目前有一个泛型接口和一个泛型类型,分别是ISignal<T>SignalValue<T>。但是,我对 SignalValue 的 <T> 有一个限制,要求 T 具有可比性。

每个的简化版本如下:

public interface ISignal<T> where T : IComparable
{
    string FullName { get; }
    public void SetValue(SignalValue<T> value, bool withLog = true);
    public SignalValue<T> GetValue(int readDelayMs = 500);
}

public class SignalValue<T> : IEquatable<T>, IComparable<T>, IComparable, ISignalValue where T : IComparable
{
    public readonly T Value;

    public SignalValue(T value)
    {
        Value = value;
    }

    public virtual int CompareTo(T other)
    {
       return Value.CompareTo(other);
    }
}

SignalValue&lt;T&gt; 主要用作 T 是 bool 或 double 的值的包装器,并带有一些附加的属性。然而,一旦我有了这个约束,我发现由于装箱约束正在到处传播。例如,我有以下静态方法,它显然也需要约束,即使它对任何与比较相关的内容没有真正的依赖关系:

public static void DoSequence<T>(ISignal<T> signal, List<SequencePair<T>> pair) where T : IComparable
{
      if (signal == null) throw new ArgumentNullException(nameof(signal));
      if (pair == null) throw new ArgumentNullException(nameof(pair));
            
      if (pair.Count <= 0) return;

       List<SequencePair<T>> orderedList = pair.OrderBy(p => p.Seconds).ToList();
       //... more stuff but no comparison at all
 }

我理解为什么需要这个约束,但这也让我想知道在我的类中使用对泛型类型的约束来设置它是否是代码异味。有没有什么方法可以让我的类在使用 SignalValue&lt;T&gt; 的地方仍然需要一个可比较的类型,而不需要在整个代码中进行约束?

【问题讨论】:

  • “我明白为什么需要这个约束” -- 那么你也明白为什么你需要在任何你使用SignalValue&lt;T&gt;的地方提供这个约束。如果您不提供约束,编译器应该如何验证您的类型参数与SignalValue&lt;T&gt; 一起使用是否合法。是什么让你认为你应该能够摆脱它?
  • 我不认为我应该能够逃脱它 - 这就是为什么句子的第二部分说“......想知道是否通过对泛型类型的约束来设置它在我的班级里有一种代码味道”,这意味着我想知道是否有更好的方法。也许我应该更笼统,只是问“有没有更好的方法”。我在最后一句中这样做了,但我想我在询问该类是否需要该类型时过于具体。无论如何,@JonasH 在下面回答这个问题。
  • 如果DoSequenceSignalValue&lt;T&gt; 内部的静态方法,那么您不需要重新声明T

标签: c# generics constraints


【解决方案1】:

在大多数情况下,通用约束可以通过委托给其他类型来代替。对于将是IComparer&lt;T&gt; 的比较。因此,如果您的方法需要比较,您可以添加一个 IComparer 参数。为方便起见,您还可以使用泛型约束添加重载,并使用 Comparer&lt;T&gt;.Default 并将实现委托给 main 方法。

大多数与排序相关的方法都可以采用 IComparer 对象,这通常是比实现 IComparable 更灵活的解决方案,因为只能有一个 IComparable 实现。

在您的示例中,SignalValue&lt;T&gt; 具有公共 Value,因此实际上不需要 CompareTo 方法。任何想比较值的人都可以做mySignalValue.Value.CompareTo(myOtherSignalValue.Value),不需要实现IComparable。

【讨论】:

    猜你喜欢
    • 2016-12-11
    • 1970-01-01
    • 1970-01-01
    • 2013-04-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多