Click here to Skip to main content
15,892,768 members
Please Sign up or sign in to vote.
4.00/5 (2 votes)
See more:
Could someone help me to understand if what I want to do is possible?

I have a simple generic method:

C#
public T DivideByTwo<T>(T number)
{
     T two = (T)Convert.ChangeType(2, typeof(T));

     T result = DIVIDE(number, two);

     return result;
}


I can call it like this no problem:


C#
ParallelNumber<double> parallelNumber = new ParallelNumber<double>();

double result = parallelNumber.DivideByTwo(3);


And of course I can use int,uint etc etc.

But I cannot call it as follows:

C#
ParallelNumber<BigInteger> parallelNumber = new ParallelNumber<BigInteger>();

BigInteger result = parallelNumber.DivideByTwo(3);


The line which fails is:

T two = (T)Convert.ChangeType(2, typeof(T));

Throws InvalidCastException. Invalid cast from System.Int32 to System.Numerics.BigInteger

Can I make my method generic enough to handle BigInteger?

(The DIVIDE function just takes two generic arguments and returns a generic)
Posted
Updated 11-Feb-13 6:12am
v2
Comments
Sergey Alexandrovich Kryukov 11-Feb-13 12:49pm    
You are posing an interesting problem. I vote 4 for the answer.

You are trying to create a numeric library (even if this is one function, but essentially this is what it is) abstracted from the concrete primitive numeric types.
I can tell you that existing solutions of this problems for .NET are all quite ugly. This is one of the few fundamental limitations of the .NET, related to 1) lack of fully-fledged meta-types, 2) limitation of generic constraints (there is not a base class or interface "numeric type", and primitive types cannot be used for generic constraints).

One solution is to pass two generic parameters: numeric type and class implementing the interface with common arithmetic operations, so the generic constraint will be this interface. If this numeric type and interface implementation do not match, nothing can prevent it, it will compile but fail to work. And this is the best solution...

If you are really interested, I can explain it all, with the available solution, but my advice is to give up. Seriously.
—SA
Mehdi Gholam 11-Feb-13 13:25pm    
Agreed, some abstract problems are not worth the effort when there are simpler ways to divide by 2 :)
Sergey Alexandrovich Kryukov 11-Feb-13 14:33pm    
Well, actually, making libraries abstracted from the type is extremely good to have, but as to numeric... Even C++ puts .NET to shame. With .NET, it hardly pays off, well, may be for some specific numeric calculation topics...
—SA

XML
Hi,

Thanks for all the answers. Here's my solution so far. It is simpler to code than the accepted solution recommended by AspDotNetDev, and I think would be faster. Can anyone advise me further or is what I have as good as it will get?

I have a generic class defined as follows:

 public class ParallelNumber<T>
 {
     private T GENERIC_TWO;

     private Func<T,T,T> DIVIDE = null;

      public ParallelNumber()
      {
            CreateDivideFunction();

            if (typeof(T) == typeof(BigInteger))
            {
                GENERIC_TWO = (T)Convert.ChangeType((BigInteger)2, typeof(T));
            }
            else
            {
                GENERIC_TWO = (T)Convert.ChangeType(2, typeof(T));
            }
      }

      private void CreateDivideFunction()
      {
            ParameterExpression paramA = Expression.Parameter(typeof(T), "a"), paramB = Expression.Parameter(typeof(T), "b");

            BinaryExpression body = Expression.Divide(paramA, paramB);

            DIVIDE = Expression.Lambda<Func<T, T, T>>(body, paramA, paramB).Compile();
       }

       public T DivideByTwo(T number)
       {
            T result = DIVIDE(number, GENERIC_TWO);

            return result;
       }
 }

The last function DivideByTwo is the one which can now be called with any numeric type.

Would anyone else endorse this method and if so would they suggest improvements or would anyone go down a completely different root.

This method takes about 100 times longer to run than one written using (say) uint instead of generics. Can I hope to match performance using generics instead on (say) uint.

It would be ideal if at runtime, after having used the above function as follows:

ParallelNumber<uint> parallelNumber = new ParallelNumber<uint>();
uint d = parallelNumber.DivideByTwo(50);

it ran as fast as calling the function written with uint instead of generics. But then of course it ran slower when called as:

ParallelNumber<BigInteger> parallelNumber = new ParallelNumber<BigInteger>();
BigInteger d = parallelNumber.DivideByTwo(50);

Thanks for any help,
Mitch.
 
Share this answer
 
XML
 HERE's the rest of the text which the preview ingored :(

(body,paramA,paramB).Compile();
       }

       public T DivideByTwo(T number)
       {
            T result = DIVIDE(number, GENERIC_TWO);

            return result;
       }
 }

The last function DivideByTwo is the one which can now be called with any numeric type.

Would anyone else endorse this method and if so would they suggest improvements or would anyone go down a completely different root.

This method takes about 100 times longer to run than one written using (say) uint instead of generics. Can I hope to match performance using generics instead on (say) uint.

It would be ideal if at runtime, after having used the above function as follows:

ParallelNumber<uint> parallelNumber = new ParallelNumber<uint>();
uint d = parallelNumber.DivideByTwo(50);

it ran as fast as calling the function written with uint instead of generics. But then of course it ran slower when called as:

ParallelNumber<BigInteger> parallelNumber = new ParallelNumber<BigInteger>();
BigInteger d = parallelNumber.DivideByTwo(50);

Thanks for any help,
Mitch.
 
Share this answer
 
Comments
RajeshRaushan 19-Feb-13 4:29am    
You are creating dynamic type on your own. The problem here is with the creation. Every time you create an Instance it creates a code snippet and compile - all these just to get a proper function definition for the supplied T type.

Just maintain the private Func<T,T,T> as static. In the Instance constructor see if it is null.
If so do what you are doing - otherwise no need to create and compile that once again. the function would be there already for the supplied type.

This way for a given type T - you will not end-up creating function dynamically every time.

C# generics are different from C++ templates. It doesn't provide the exact thing here - but this kind of usage in real world example is extremely rare. Also this isn't best way how we should work with Generics.
It will work for numeric types - but if somebody starts using like ParallelNumber<string> or ParallelNumber<employee>.

It will throw a runtime exception and that is not c# way of programming for sure. C++ is a language for intelligent developers - where a lot depend upon intelligence of the programmer - where as C# is a intelligent language in itself. It puts best effort to make sure things doesn't get wrong and surprises are not travelling directly to end-user.
I recommend the following approach.

  1. Detect if Convert.ChangeType will work. You might just try it and catch the exception (that'd be the easiest way). However, there may be a way to use IConvertible to check the conversion without an exception being thrown. I'll leave this to you to dive deeper into.
  2. If Convert.ChangeType doesn't work, use reflection to check if the type has a constructor that accepts an integer. You can then use reflection to create an instance of that type and pass the integer to the constructor. This may also be possible with expression trees, but I'm not really sure how that works (though that may be a more optimal approach).
  3. If there is no appropriate constructor, you may want to use reflection to check if there is an implicit conversion operator from an int, then use reflection to perform the assignment to a new instance of the type.
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900