을 만들 수학 라이브러리를 사용하여 제네릭 C#
문제
가 실현 가능한 방법을 사용하여 제네릭을 만들 수학 라이브러리에 의존하지 않는 기본 유형을 선택한 데이터를 저장하는 데?
다시 말해서,다고 가정하자 나는 일부를 작성하려 클래스입니다.분율로 표시될 수 있 두 개의 수 또는 두 개의 복식 또는었습니다.중요한 것은 기본적인 네 개의 연산은 제대로 정의되어 있습니다.그래서,내가 쓸 수 있을 것이다 Fraction<int> frac = new Fraction<int>(1,2)
그리고/또는 Fraction<double> frac = new Fraction<double>(0.1, 1.0)
.
불행하게도 없습니다 인터페이스를 대표하는 네 개의 기본 작업(+,-,*,/).는 사람을 찾을 실행할 수 있는 가능한 방법으로 구현하는 이?
해결책
방법은 여기를 추상적으로 사업자는 상대적으로 고통입니다.
abstract class MathProvider<T>
{
public abstract T Divide(T a, T b);
public abstract T Multiply(T a, T b);
public abstract T Add(T a, T b);
public abstract T Negate(T a);
public virtual T Subtract(T a, T b)
{
return Add(a, Negate(b));
}
}
class DoubleMathProvider : MathProvider<double>
{
public override double Divide(double a, double b)
{
return a / b;
}
public override double Multiply(double a, double b)
{
return a * b;
}
public override double Add(double a, double b)
{
return a + b;
}
public override double Negate(double a)
{
return -a;
}
}
class IntMathProvider : MathProvider<int>
{
public override int Divide(int a, int b)
{
return a / b;
}
public override int Multiply(int a, int b)
{
return a * b;
}
public override int Add(int a, int b)
{
return a + b;
}
public override int Negate(int a)
{
return -a;
}
}
class Fraction<T>
{
static MathProvider<T> _math;
// Notice this is a type constructor. It gets run the first time a
// variable of a specific type is declared for use.
// Having _math static reduces overhead.
static Fraction()
{
// This part of the code might be cleaner by once
// using reflection and finding all the implementors of
// MathProvider and assigning the instance by the one that
// matches T.
if (typeof(T) == typeof(double))
_math = new DoubleMathProvider() as MathProvider<T>;
else if (typeof(T) == typeof(int))
_math = new IntMathProvider() as MathProvider<T>;
// ... assign other options here.
if (_math == null)
throw new InvalidOperationException(
"Type " + typeof(T).ToString() + " is not supported by Fraction.");
}
// Immutable impementations are better.
public T Numerator { get; private set; }
public T Denominator { get; private set; }
public Fraction(T numerator, T denominator)
{
// We would want this to be reduced to simpilest terms.
// For that we would need GCD, abs, and remainder operations
// defined for each math provider.
Numerator = numerator;
Denominator = denominator;
}
public static Fraction<T> operator +(Fraction<T> a, Fraction<T> b)
{
return new Fraction<T>(
_math.Add(
_math.Multiply(a.Numerator, b.Denominator),
_math.Multiply(b.Numerator, a.Denominator)),
_math.Multiply(a.Denominator, b.Denominator));
}
public static Fraction<T> operator -(Fraction<T> a, Fraction<T> b)
{
return new Fraction<T>(
_math.Subtract(
_math.Multiply(a.Numerator, b.Denominator),
_math.Multiply(b.Numerator, a.Denominator)),
_math.Multiply(a.Denominator, b.Denominator));
}
public static Fraction<T> operator /(Fraction<T> a, Fraction<T> b)
{
return new Fraction<T>(
_math.Multiply(a.Numerator, b.Denominator),
_math.Multiply(a.Denominator, b.Numerator));
}
// ... other operators would follow.
}
당신이 실패하는 경우를 구현하는 형식을 사용하여,당신은 당신을 얻을 것이 실패하는 대신 런타임에 컴파일 시간에(나쁜).의 정의 MathProvider<T>
구현상이 될 것 같은(나).는 것이 좋습니다 것입니다 당신이 피하기만 하 C#고 사용하 F#또는 다른 언어 더 이 추상화 수준.
편집: 정의 정의를 추가하고 빼기 Fraction<T>
.또 다른 흥미와 간단한 일이지를 구현하 MathProvider 에서 작동하는 추상적이 구문을 나무입니다.이것이 바로 포인트를 하는 것이 자동으로 차별화: http://conal.net/papers/beautiful-differentiation/
다른 팁
저는 이것을 믿는 당신의 질문에 대답:
여기는 미묘한 문제는 함께 제공됩니다.가정 알고리즘을 포함한 부문,말할 가우시안 해결하기 위하여 제거 방정식의 시스템.당신이 통과하면서 정수,당신은 잘못된 응답을 얻을 것이기 때문에 당신은 수행 정수 division.그러나 전달하는 경우에 두 번 인수하는 일이 일어날 정수 값을 얻을 것이다,당신은 정답이 있습니다.
같은 일이 일어난 사각형 뿌리에서와 같이,Cholesky 분해.고려해 정수 매트릭스는 잘못 가는 반면,인수 분해하는 행렬의 두 배가하는 일이의 정수 값을 잘 될 것입니다.
첫째,클래를 제한해야 합니다 일반적인 매개 변수를 기본(공용 클래스의 일부분 여기서 T:구조체,새로운()).
두번째,당신은 아마를 만들어야 암시적 오버로드 캐스팅 할 수 있도록 핸들을 캐스팅에서 하나 입력하지 않고 다른 컴파일러에서 울고있다.
셋째,당신할 수 있는 하중 초과의 네 가지 기본적인 연산자를 만들뿐 아니라 인터페이스가 더 유연한 때에 결합하는 분수의 다른 유형이 있습니다.
마지막으로,당신은 고려하는 어떻게 처리하는 산수 및 언더플로우.좋은 라이브러리가 매우 명시적으로 처리하는 방법을 오버플로우;그렇지 않으면 믿을 수 없어 결과의 작업의 다른 부분 유형입니다.