C# 创建计算数值T的平均值的泛型类

C# 创建计算数值T的平均值的泛型类,c#,.net,generics,C#,.net,Generics,我需要创建一个类 Average<T> 平均值 它包含3种方法: T average()-返回T值集合的平均值 添加(T项)-将项添加到T值集合中 reset()-将平均实例重置为其默认状态 T必须是数字(int、float、byte等) 根据我的理解,我必须创建基本抽象类或接口: public abstract class NumericBase<T>{} 公共抽象类NumericBase{} 我的派生类继承的: public class Numeric

我需要创建一个类

Average<T> 
平均值
它包含3种方法:

  • T average()-返回T值集合的平均值
  • 添加(T项)-将项添加到T值集合中
  • reset()-将平均实例重置为其默认状态
T必须是数字(int、float、byte等)

根据我的理解,我必须创建基本抽象类或接口:

public abstract class NumericBase<T>{}

公共抽象类NumericBase{}
我的派生类继承的:

public class NumericInt:NumericBase<int>{} 
公共类NumericInt:NumericBase{}
之后,我应该创建我的平均类:

public class Average<T> where T: NumericBase<T>  
公共类平均值,其中T:NumericBase
但我猜最后一步是错误的,因为我必须在Average类中创建方法add(T item),如果我正确理解此任务,它的工作方式必须如下所示:

Average<int> av = new Average();
av.add(3);
av.add(4);
int averageValue = av.average();
av.reset();
Average av=新平均值();
av.add(3);
av.add(4);
int averageValue=av.average();
av.reset();

我一整天都在试图解决这个问题,所以我什么也没做。有人能帮我吗?

考虑以下几点:


  • 对于数字没有这样的通用约束
  • 当您想到一个仅限于2-3个类型的泛型约束时,通常意味着您不需要泛型类型,而是需要创建2-3个不同的类
我不确定您是否真的需要使用泛型类型,但如果出于学习目的,您只想使用泛型、列表和可枚举类方法,您可以在构造函数中检查
T
的类型,如果它不是允许的类型之一(例如int和double),则抛出异常

using System;
using System.Collections.Generic;
using System.Linq;
public sealed class MyBadClass<T>
{
    List<T> list;
    public MyBadClass()
    {
        var allowedTypes = new[] { typeof(int), typeof(double), typeof(float) };
        if (!allowedTypes.Contains(typeof(T)))
            throw new Exception($"Type '{typeof(T)}' not supported.");
        list = new List<T>();
    }
    public double Average()
    {
        return list.Cast<double>().Average();
    }
    public void Add(T value)
    {
        list.Add(value);
    }
    public void Reset()
    {
        list.Clear();
    }
}
使用系统;
使用System.Collections.Generic;
使用System.Linq;
公共密封类MyBadClass
{
名单;
公共MyBadClass()
{
var allowedTypes=new[]{typeof(int)、typeof(double)、typeof(float)};
如果(!allowedTypes.Contains(typeof(T)))
抛出新异常($“不支持类型“{typeof(T)}”);
列表=新列表();
}
公众双倍平均
{
返回列表.Cast().Average();
}
公共无效添加(T值)
{
列表。添加(值);
}
公共无效重置()
{
list.Clear();
}
}

看起来您有语法错误。您缺少应该位于“where T”语句后面的泛型约束

而不是:
公共类平均值,其中T:NumericBase
, 试试这个:
公共类平均值,其中T:struct,NumericBase


那里的结构用作泛型类型的约束。按照此链接查看详细信息

您的约束是错误的,无法使用
T:NumericBase
,它将是一种无限递归类型

不幸的是,C#没有“数值”类型的通用约束,这将允许您将
T
的实例添加在一起或除以一个数字。您不能将约束表示为“这是一个数字”,但有一种方法可以将约束
T
转换为一个数字(以及一系列其他类型)

公共类平均值,其中T:IConvertible
{
公共作废新增(T项)
{
double converted=item.ToDouble(null);
...
}
}
这样,您将自己限制为双精度浮点,任何试图将该类与自定义类型一起使用的人都必须实现整个过程。我不确定这个界面是如何使用的,但这到底是怎么回事

如果不放弃您的要求,
int
T
的正确参数,几乎没有其他方法可以做到这一点。如果我们放弃这一点,那么整个世界的可能性就会打开:

公共接口IAverageable
{
TValue相加(TValue other);
t平均除以计数(整数计数);
}
public struct AverageableInt:IAverageable
{
私有只读int;
公共平均整数(整数n)=>\u n=n;
公共AverageInt相加(AverageInt其他)=>
新的AverageInt(此._n+其他._n);
公共双除法计数(整数计数)=>(双除法计数);
}
公共类平均值,其中TValue:iAverable
{
…//实现。
}
var average=新平均值();
average.Add(新的AverageableInt(3));
添加(新的AverageableInt(4));
...
通过在
int
AverageableInt
之间引入用户定义的转换,您可以使它更整洁一些:

//内部AverageableInt。
公共静态隐式运算符AverageableInt(int n)=>新的AverageableInt(n);
var average=新平均值();
平均数。增加(3);
平均数。增加(4);
...

它需要您添加大量自定义类型,以使其适用于每个内置类型,但这是一个有效的类型安全解决方案。

请注意,将
T
约束为数字类型是不可能的,并且不允许将类命名为与属性
Average
相同的类。所以我给我的类命名为
Statistics

为了简化派生类的实例化,您需要一个非泛型类作为工厂。因此,不是
newstatistics.IntStatistics()
调用
Statistics.Int()

静态类程序
{
静态void Main(字符串[]参数)
{
//开始整数统计
var int_ave=Statistics.int();
国际大道加(3);
国际大道加(5);
国际大道增补(7);
Assert(int_ave.Count==3);
断言(int_ave.Average==(3+5+7)/3);
//开始浮点统计
var float_ave=Statistics.float();
浮动平均添加范围(2f、4f、7f、9f);
Assert(float_ave.Count==4);
断言(float_ave.Average==(2f+4f+7f+9f)/4);
}
}
/// 
///工厂
/// 
公共静态类统计
{
public static Statistics Byte()=>new Statistics.B
static class Program
{
    static void Main(string[] args)
    {
        // start integer statistics
        var int_ave = Statistics.Int();
        int_ave.Add(3);
        int_ave.Add(5);
        int_ave.Add(7);

        Debug.Assert(int_ave.Count == 3);
        Debug.Assert(int_ave.Average == (3+5+7)/3);

        // start float statistics
        var float_ave = Statistics.Float();
        float_ave.AddRange(2f, 4f, 7f, 9f);
        Debug.Assert(float_ave.Count == 4);
        Debug.Assert(float_ave.Average == (2f+4f+7f+9f)/4);
    }

}
/// <summary>
/// Factory
/// </summary>
public static class Statistics
{
    public static Statistics<byte> Byte() => new Statistics<byte>.ByteStatistics();
    public static Statistics<int> Int() => new Statistics<int>.IntStatistics();
    public static Statistics<float> Float() => new Statistics<float>.FloatStatistics();
    public static Statistics<double> Double() => new Statistics<double>.DoubleStatistics();
    public static Statistics<decimal> Decimal() => new Statistics<decimal>.DecimalStatistics();
}
/// <summary>
/// Base class
/// </summary>
public abstract class Statistics<T> where T : struct, IComparable<T>
{
    public T Average { get; private set; }
    public int Count { get; private set; }

    /// <summary>
    /// When overidden in derived classes the item is considered
    /// and a new average is computed. <see cref="Count"/> is 
    /// also incremented.
    /// </summary>
    /// <param name="item">The numeric value to add.</param>
    public abstract void Add(T item);

    /// <summary>
    /// Adds multiple values
    /// </summary>
    public void AddRange(IEnumerable<T> list)
    {
        foreach (var x in list)
        {
            Add(x);
        }
    }
    /// <summary>
    /// Adds multiple values
    /// </summary>
    public void AddRange(params T[] list)
    {
        AddRange(list.AsEnumerable());
    }
    /// <summary>
    /// Resets the statistics.
    /// </summary>
    public void Reset()
    {
        this.Average = default(T);
        this.Count = 0;
    }

    /// <summary>
    /// Derived class for byte
    /// </summary>
    internal class ByteStatistics : Statistics<byte>
    {
        public override void Add(byte item)
        {
            Average = (byte)((Count*Average + item)/(Count+1) % 256);
            Count += 1;
        }
    }
    /// <summary>
    /// Derived class for int
    /// </summary>
    internal class IntStatistics : Statistics<int>
    {
        public override void Add(int item)
        {
            Average = (Count*Average + item)/(Count+1);
            Count += 1;
        }
    }
    /// <summary>
    /// Derived class for float
    /// </summary>
    internal class FloatStatistics : Statistics<float>
    {
        public override void Add(float item)
        {
            Average = (Count*Average + item)/(Count+1);
            Count += 1;
        }
    }
    /// <summary>
    /// Derived class for double
    /// </summary>
    internal class DoubleStatistics : Statistics<double>
    {
        public override void Add(double item)
        {
            Average = (Count*Average + item)/(Count+1);
            Count += 1;
        }
    }
    /// <summary>
    /// Derived class for decimal
    /// </summary>
    internal class DecimalStatistics : Statistics<decimal>
    {
        public override void Add(decimal item)
        {
            Average = (Count*Average + item)/(Count+1);
            Count += 1;
        }
    }
}