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()-将平均实例重置为其默认状态
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;
}
}
}