开发者

How to mix generic and real types

Why is is not allowed to have generic class like this :

class SomeClass<T, int> { ..... }

Also is it possible to force some type T to be 开发者_JAVA技巧numeric and support basic operators like +, -, * and /.


Because generics are not the same as C++ templates; which allow for partial specialisation.

Of course you can have this:

public class Generic<T, U> {}

public class Generic2<T> : Generic<T, int> {}

So long as any generic constraints present on T in the first generic are at least repeated or more restrictive on the second.

As for the restrictions you ask for, no, you can't. You can, however restrict to value types (where T : struct of course); but that's not really restricting to 'numeric'.

Operators are not included as part of a class' contract (either by base or interface) because of the fact that operators are defined statically and, for the expression a op b, can appear on either of the two types T(a) or T(b).

As a result, it's impossible to define type constraints based on them.

However, you can define your own contract - like this (naive and totally incomplete implementation here, just to demonstrate, in a wall of code(tm)):

(Check out the generic at the very end to see that it works)

public abstract class MyNumeric
{
  public abstract object ValueObject { get; }
  public abstract MyNumeric Add(MyNumeric other);
  public abstract MyNumeric Substract(MyNumeric other);
  public abstract MyNumeric Multiply(MyNumeric other);
  public abstract MyNumeric Divide(MyNumeric other);

  public static MyNumeric operator +(MyNumeric a, MyNumeric b)
  {
    return a.Add(b);
  }
  public static MyNumeric operator -(MyNumeric a, MyNumeric b)
  {
    return a.Substract(b);
  }
  public static MyNumeric operator *(MyNumeric a, MyNumeric b)
  {
    return a.Multiply(b);
  }
  public static MyNumeric operator /(MyNumeric a, MyNumeric b)
  {
    return a.Divide(b);
  }
  //etc
}

public abstract class MyNumeric<T> : MyNumeric
  where T : struct
{
  public override object ValueObject { get{ return this.Value;}}
  public new T Value { get; private set; }
  public MyNumeric(T value) { Value = value; }
}

public class MyInt : MyNumeric<int>
{
  public MyInt(int value) : base(value) { }
  public override MyNumeric Add(MyNumeric other)
  {
    //could be really crafty here and use an interface instead that
    //gives access to the Value part only - that way you could
    //have MyDouble, for example, implement INumeric<int> explicitly 
    //via a c# explicit conversion.
    MyNumeric<int> otherInt = other as MyNumeric<int>;
    if (otherInt == null)
      throw new ArgumentException(
        "Need to handle numeric promotion/demotion for all types", "other");
    return new MyInt(Value + otherInt.Value);
  }
  public override MyNumeric Divide(MyNumeric other)
  {
    throw new NotImplementedException();
  }
  public override MyNumeric Multiply(MyNumeric other)
  {
    throw new NotImplementedException();
  }
  public override MyNumeric Substract(MyNumeric other)
  {
    throw new NotImplementedException();
  }
}

public class MyGeneric<TNumeric> where TNumeric : MyNumeric
{
  public TNumeric WillNowCompile(TNumeric a, TNumeric b)
  {
    //cast is still required on the result.
    //however - you can set the return type to MyNumeric instead if you want.
    return (TNumeric)(a + b);
  }

  public TNumeric AnotherOne<TNumeric2>(TNumeric a, TNumeric2 b)
    where TNumeric2 : MyNumeric
  {
    return (TNumeric)(a / b);
  }
}

As I say in the code, however, you will have to consider how to handle situations like int + double. But it can be done using this as a starting point.


The generic T parameter allows to to be non-specific about the parameter type that the class is using.

If you are wanting to use an int then, use an int within a class. If you are wanting to stipulate that something in the class must be an int then do it via a parameter to an public method or the constructor.

The support for the operators will be on the type it self being passed in. If you have overriden the operator for the type then it will supprt it once used within a class. You cannot do so on an unknown generic.


For the second part of the question you can do :

class MyClass<T>  where T : IEquatable<T>, IComparable<T>

There is no parent class for all numerics, buts they do implement those interfaces.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜