2010-12-01 53 views
22

我最近遇到了这个问题,我想要一个函数来处理双精度和整数,并且想知道为什么没有用于所有数字类型(包含算术运算符和比较)的通用接口。为什么数字类型不共享通用接口?

它会使写作功能如Math.Min(存在于gazillion重载)方式更方便。

会引入一个额外的接口是一个突破性的变化?

编辑: 我想用这个像

public T Add<T>(T a, T b) where T: INumber 
{ 
    return a+b; 
} 

public T Range<T>(T x, T min, T max) where T:INumber 
{ 
    return Max(x, Min(x, max), min); 
} 
+0

@Saeed:那点。 =)我的Add方法只是一个简单的例子。就我而言,我正在对给定数组进行一些基本的统计分析,并且不关心它们是int,double,decimal还是BigIntegers,只要我可以添加它们即可。 (并且不必在许多过载中编写这种方法) – Jens 2010-12-01 08:41:13

+0

https://referencesource.microsoft.com/#mscorlib/system/int32.cs,31 - `IArithmetic `似乎被注释掉,可悲地:( – Caramiriel 2017-12-08 12:18:08

回答

8

如果你想要做这样的“通用”算法,你在强类型语言如C#中的选项是非常有限的。 Marc Gravell described the problem as follows

.NET 2.0引入了泛型到.NET世界中,它为许多优雅的解决方案打开了大门,为现有问题打开了大门。通用约束可用于限制类型参数到已知接口等,以确保访问功能 - 或者对于简单的相等/不等测试,Comparer<T>.DefaultEqualityComparer<T>.Default单例分别实现IComparer<T>IEqualityComparer<T>(允许我们例如对元素进行排序,而无需了解任何有关问题的“T”)。

尽管如此,在运营商方面仍然存在很大差距。由于运算符声明为静态方法,因此不存在所有数字类型实现的IMath<T>或类似等效接口;事实上,运营商的灵活性会使得这一切很难以有意义的方式进行。更糟糕的是:许多原始类型的操作符甚至不存在操作符;相反,有直接的IL方法。为了使情况更加复杂,Nullable <>要求“提升运算符”的概念,其中内部“T”描述适用于可空类型的运算符 - 但这是作为语言特征实现的,并不由运行时(使反射更有趣)。

然而,C#4.0中引入的dynamic关键字,你可以用它来在运行时选择正确的过载:

using System; 

public class Program 
{ 
    static dynamic Min(dynamic a, dynamic b) 
    { 
     return Math.Min(a, b);   
    } 

    static void Main(string[] args) 
    { 
     int i = Min(3, 4); 
     double d = Min(3.0, 4.0); 
    } 
} 

你应该知道,这消除类型安全,你可能会在运行时异常如果动态运行时无法找到合适的重载调用,例如因为你混合类型。

如果你想要得到的类型安全,你可能想看看在MiscUtil库基本操作提供通用的运营商提供的类。

请注意,如果您只是在特定操作之后,实际上可能会使用内置类型已实现的接口。例如,一个类型安全的泛型Min功能看起来是这样的:

public static T Min<T>(params T[] values) where T : IComparable<T> 
{ 
    T min = values[0]; 
    foreach (var item in values.Skip(1)) 
    { 
     if (item.CompareTo(min) < 0) 
      min = item; 
    } 
    return min; 
} 
1

好了,你也看不定义接口运营商和结构(尽管它们支持的接口)止跌通过接口实现工作得很好,因为这需要装箱和取消装箱,这纯粹是通过接口实现进行数学操作时,这当然是一个巨大的性能。

我还要强调的是,当你施放一个结构到它的界面,结果对象是你,而不是原来的结构本身进行操作的引用类型(盒装对象):

interface IDoSomething 
{ 
    void DoSomething(); 
} 

struct MyStruct : IDoSomething 
{ 
    public MyStruct(int age) 
    { 
    this.Age = age; 
    } 

    public int Age; 

    pubblic void DoSomething() 
    { 
    Age++; 
    } 
} 

public void DoSomething(IDoSomething something) 
{ 
    something.DoSomething(); 
} 

当我通过我的结构实例,它的盒装(成为一个引用类型),我执行我的DoSomething操作,但我原来的结构实例不会改变。

+0

它会实际上工作,只要你“投”到一个接口,结构将被装箱。 – leppie 2010-12-01 08:38:56

+0

看到我的答案,我的意思。 – leppie 2010-12-01 08:40:36

+0

当我调用DoSomething(ISomething东西) `方法 – 2010-12-01 08:40:49

-3

这是“强类型语言”的主要特征。这可以在一分钟内避免数十亿的错误。当然,我们希望int与double是完全不同的。

4

即如何做2 + 2.35?返回4或4.35或4.349999?界面如何理解什么是适当的输出?你可以编写你的扩展方法并使用重载来解决你的问题,但是如果我们想要为所有目的设置接口,界面大​​小和查找有用函数的时间将会很困难,而接口增加了一些开销和数字通常是基础所以计算需要快捷的方式。

我觉得写的扩展类是你的情况更好:

public static class ExtensionNumbers 
{ 
    public static T Range<T>(this T input, T min, T max) where T : class 
    { 
     return input.Max(input.Min(max), min); 
    } 

    public static T Min<T>(this T input, params T[] param) where T : class 
    { 
     return null; 
    } 

    private static T Max<T>(this T input, params T[] number) where T : class 
    { 
     return null; 
    }  

} 

我以前where T : class只是要编译

0

的问题是,如何号码存储不同数量的类型的结构是治疗基金会不同。对于初学者来说,你的错误是错误的,界面不起作用,但是我认为你想要的是你想让数字松散地输入。

只是为了开始你为什么不想这么做考虑到整型类型是它们可以表示的值的范围上的一对一映射,而浮点类型有一个persistence和指数组件,因为有婴儿多数浮点数。语言开发人员必须在语言设计中做出一些非常基本且可能导致错误的假设。

看看这个article关于浮点数学的更多信息。

1

与马修的回答一起,请注意3个调用之间的差异。

void DoSomething(ref MyStruct something) 
{ 
    something.DoSomething(); 
} 

static void Main(string[] args) 
{ 
    var s = new MyStruct(10); 
    var i = (IDoSomething)s; 

    DoSomething(s); // will not modify s 
    DoSomething(i); // will modify i 
    DoSomething(ref s); // will modify s, but with no reassignment 

} 
1

它不像引入界面简单,因为可用的操作每个类型不同,但它们并非总是甚至均匀(即DateTime + TimeSpan => DateTime,DateTime - DateTime => TimeSpan)。

在技术层面,这里可能涉及很多拳击等,加上经常经营者是static

其实,对于Min/MaxComparer<T>.Default.Compare(x,y)几乎所有你会希望的。

对于其他操作符:在.NET 4中。0 dynamic是有很大帮助的位置:

T x = ..., y = ...; 
T sum = (dynamic)x + (dynamic)y; 
通过 Operator

但除此之外, “MiscUtil” 有generic support for operators,即

T x = ..., y = ...; 
T sum = Operator.Add(x, y); // actually Add<T> 
相关问题