如果你的泛函库不能使在 C# 中完成泛型数学运算,没有人会对此买单。因此,接下里让我们处理这个问题。如果不能够修改 C# 原始数据类型去实现想要的接口,那么我们就创造另一种类型能够处理那些类型具有的所有数学运算。这就是在 .Net 框架中广泛使用的标准提供者模式。
边注/发泄:就我个人来说,我憎恨提供者模式。但我至今没有发现使用委托有处理不好的例子。当大量创建大量提供者时,他们没有使用委托。
当我们使用提供者模式,本质上仍是做和以前同样的事,但一个提供者类就能处理所有的数学运算。在EXAMPLE 4中检验它:
/EXAMPLE 4 ----------------------------------------
namespace ConsoleApplication
{
public class Program
{
public static void Main(string[] args)
{
Sum<int>(new int[] { 1, 2, 3, 4, 5}, new MathProvider_int());
} // (1) all the methods need access to the provider
public static T
Sum<T>(T[] array, MathProvider<T> mathProvider)
{
T sum = mathProvider.GetZero();
for (int i = 0; i < array.Length; i++)
sum = mathProvider.Add(sum, array[i]);
return sum; }
public interface MathProvider<T>
{
T GetZero(); // (2) you still need instance factory methods
T Add(T left, T right);
}
public class MathProvider_int : MathProvider<int>
{
public MathProvider_int() { }
int MathProvider<int>.GetZero()
{
return 0;
} // (3) you still have to implement each function for every single type
int MathProvider<int>.Add(int left, int right)
{
return left + right;
}
}
} // (4) can be slow depending on implementation (this version is slow)
EXAMPLE 4 通过把所有的泛函性质移动到帮助类中,我们可以使用C#基本类型执行数学运算。然而,这仅仅修复 EXMAPLE 3 中的第一个问题。我们仍旧需要解决以下问题:
所有方法都必须访问 mathProvider 类。虽然您可以编写代码,让其不必在每个函数间传递,这个原则同样适用于其它类似的结构。
你的实例化仍然基于工厂方法。在上面的情况中它是一个来自于int的转换。
在原始代码中你仍然需要为每一个简单的类型中实现泛函性。
这仍然相当慢,除非你为 provider 做一些”聪明的“缓存。provider 的传递和查找加起来真的很多。
现在我们已经尝试过在数值类型本身(EXAMPLE 3)和外部 provider(EXAMPLE 4)上使用接口。使用接口我们已经不能做更多了。可以确定的是我们可以运用一些聪明巧妙的存储方法,但最终仍会面临相同的问题:必须在每一步都支持定制的实现。
最后说一句...在 C# 中接口不适合用在高效的泛函计算中。
对象转换在 C# 中所有事物都可以转换成 System.Object 类型。因此,我只要把每一个事物转换成一个对象然后用控制流处理它就可以了。让我们试一试。
Hide Shrink Copy Code