0

I am trying to build a method that will take a variety of numeric types and preprocess them for a second method. I am not sure if I should be simply overloading or using a generic method. I tried to use a generic method but the method does not seem to recognize the parameter type. The code is below. Can someone please explain to me whether it is better to overload or use a generic method in this case? Also, if I wanted to use a generic method to do this, how could I make it work? Thank you very much.

 public static class math
 {
      public static int nextpow2<T>(T a)
      {
           double w;
           if ( a.GetType() is sbyte   ||
                a.GetType() is byte    ||
                a.GetType() is short   ||
                a.GetType() is ushort  ||
                a.GetType() is int     ||
                a.GetType() is uint    ||
                a.GetType() is long    ||
                a.GetType() is ulong   ||
                a.GetType() is float   ||
                a.GetType() is double  ||
                a.GetType() is decimal
           ) w = (double)Convert.ChangeType(a, typeof(double));
           else
                throw new System.ArgumentException("Internal error in nextpow2: argument a is not a number!");

           return _nextpow2(w);
      }

      private static int _nextpow2(double a)
      {
           double index = Math.Abs(a);
           int p = (index > 1) ? (int)Math.Ceiling( Math.Log( index, 2.0) ) : 0;

           return p;
      }

I am calling the method as follows:

 int indx = 0;
 int p = math.nextpow2(indx);

The code fails to compile. I get the following error:

Internal error in nextpow2: argument a is not a number!

Can someone please explain what I am doing wrong? Thank you.

4

2 回答 2

3

Can someone please explain what I am doing wrong?

Sure. You're checking whether a Type object is an sbyte, or a byte, etc. You're not checking whether the type represents the type of sbyte etc... you're asking whether the value is an sbyte. That's never, ever the case. (Which is why you're getting a compile-time error.)

You could use:

if (a.GetType() == typeof(byte) ||
    // etc)

But I probably wouldn't. I wouldn't make this a generic method at all. If you really want the functionality, I'd write:

private static readonly HashSet<Type> ValidTypes = new HashSet<Type>
{
    typeof(sbyte), typeof(byte), /* etc */
};

public static double ConvertToDouble(object x)
{
    if (x == null)
    {
        throw new ArgumentNullException("x");
    }
    if (!ValidTypes.Contains(x.GetType())
    {
        throw new ArgumentException("...");
    }
    return Convert.ChangeType(x, typeof(double));
}

Note how this is doing one thing: converting the argument to double. There's no point in making it then call _nextpow2 - it's easy enough for the caller to do that if they want to.

Additionally, you've written two contradictory things:

The code fails to compile. I get the following error:
Internal error in nextpow2: argument a is not a number!

If it fails to compile (which is what I'd expect, btw) then you can't be running the code, which means you can't be getting the exception. You need to differentiate between compile-time errors like this:

warning CS0184: The given expression is never of the provided ('sbyte') type

and execution-time exceptions.

Also, both your class and method names violate normal .NET naming conventions.

于 2013-03-19T19:08:50.500 回答
2

You can either say

a is sbyte

or say

a.GetType() == typeof(sbyte)

but it makes no sense to mix it into a.GetType() is sbyte. You should get a compiler warning about that! For a.GetType() will be an object which derives from System.Type (actually it will be System.RuntimeType), and therefore it can never derive from sbyte (since sbyte is sealed, being a struct, and sbyte itself derives only from ValueType and Object).

A better solution than a generic method, is to have:

public static int Nextpow2(double a)
{
    ...
}

All numereric types except decimal are implicitly convertible to double. So with the above signature, you can use it the way you want:

int indx = 0;
int p = math.Nextpow2(indx);
于 2013-03-19T19:14:14.747 回答