C#通用算法将参数约束为数字

C# Generic Algorithm constraining parameter to number

我正在写一个通用的roulette selection algorithm。通常,property 是原始数字类型,可用于每个元素的 "score" 的求和。

但是,由于实现是通用的,并且无法将 属性 的类型直接限制为数字基元,因此我没有明确的方法来对值求和 select 与 property 的值成比例。

在下面的代码中,您会注意到我试图将 属性 的值添加到 sumrouletteSum。此代码会产生错误,因为 PropertyInfo.GetValue() returns 一个对象,我无法在不破坏实现的通用性的情况下将其转换为原始数字类型。

我可以采取什么方法来确保算法的通用性,同时仍然能够按比例比较 select 所提供的 property?

一个考虑是将 P 限制为 IConvertible,但我想这会导致在 property 参数中提供原语时出现一些丑陋的类型转换。

public class RouletteSelectionFunction : ISelectionFunction
{
  public string Name => "Roulette";

  public T Select<T, P>( IEnumerable<T> elements, Expression<Func<T, P>> property )
    where T : class
  {
    var prop = ( PropertyInfo ) ( ( MemberExpression ) property.Body ).Member;

    // Sum all fitnesses and normalize negatives
    // by shifting range to minimum of 0
    double sum = 0.0;
    double lowest = 0.0;
    for ( var i = 0; i < elements.Count(); i++ )
    {
      var value = prop.GetValue( elements.ElementAt( i ) );
      sum += value;
      if ( value < lowest )
        lowest = value;
    }
    lowest = Math.Abs( lowest );
    sum += lowest * elements.Count();

    // Roll roulette and select victor
    double rouletteSum = 0;
    double random = RandomGen.NextDouble() * sum; //RandomGen wraps Random() class and NextDouble() returns number between 0 and 1
    for( var i = 0; i < elements.Count(); i++ )
    {
      rouletteSum += prop.GetValue( elements.ElementAt( i ) );
      if ( random <= rouletteSum )
        return elements.ElementAt( i );
    }

    throw new SelectionFailedException( "Roulette Selection could not determine victor" );
  }
}

// Call via:
// RouletteSelectionFunction.Select( elements, x => x.Score )

What approach can I take to assure genericness of the algorithm while still being able to proportionally compare and select from the values of the provided property?

你不会,至少不容易。 C# 从未提供适用于算术抽象的泛型类型系统。

多年来有很多提案。例如,您可以想象在接口中允许静态成员,然后您可以说 where T : IAddable<T>,其中 IAddable<T> 是一个承诺在 T 上有一个 public static T operator +(T, T) 的接口。

您还可以显式传递一个 Func<T, T, T> 来实现您的总和,等等。

但是您面临的问题本质上是您希望滥用泛型以形成实际上不是 泛型 的专业化。我们认为泛型类似于 List<T>,您可以在其中真正、真正地列出 任何类型 。您的代码实际上是通用的吗?听起来它可以通过简单地说总和为 double 来工作。