tags:

views:

6724

answers:

16

Can anyone tell me if there is a way with c# generics to limit a type T to only

Int16, Int32, Int64, UInt16, UInt32, UInt64

I'm aware of the where keyword, but can't find an interface for only these types,

Something like:

static bool IntegerFunction<T>(T value) where T : INumeric

Thanks

+1  A: 

There is no single interface or base class that they all inherit (that is not also inherited by other classes) so the simple answer is no.

I do wonder why this is an issue though. What are you wanting to do inside your IntegerFunction class that can only be done to integers?

samjudson
+4  A: 

There's no constraint for this. It's a real issue for anyone wanting to use generics for numeric calculations.

I'd go further and say we need

static bool GenericFunction<T>(T value) 
    where T : operators( +, -, /, * )

Or even

static bool GenericFunction<T>(T value) 
    where T : Add, Subtract

Unfortunately you only have interfaces, base classes and the keywords struct (must be value-type), class (must be reference type) and new() (must have default constructor)

You could wrap the number in something else (similar to INullable<T>) like here on codeproject.


You could apply the restriction at runtime (by reflecting for the operators or checking for types) but that does lose the advantage of having the generic in the first place.

Keith
I wonder if you've seen MiscUtil's support for generic operators... http://www.yoda.arachsys.com/csharp/miscutil/usage/genericoperators.html
Marc Gravell
Yeah - Jon Skeet pointed me at them for something else a while ago (but after this year old response) - they're a clever idea, but I'd still like proper constraint support.
Keith
+1  A: 

Probably the closest you can do is

static bool IntegerFunction<T>(T value) where T: struct

Not sure if you could do the following

static bool IntegerFunction<T>(T value) where T: struct, IComparable
, IFormattable, IConvertible, IComparable<T>, IEquatable<T>

For something so specific, why not just have overloads for each type, the list is so short and it would possibly have less memory footprint.

Haacked
+1  A: 

What are you wanting to do inside your IntegerFunction class that can only be done to integers?

More to the point, Why do you need a generic method for if you just just allows integers?

Jorge Córdoba
+1  A: 

I was wondering the same as samjudson, why only to integers? and if that is the case, you might want to create a helper class or something like that to hold all the types you want.

If all you want are integers, don't use a generic, that is not generic; or better yet, reject any other type by checking its type.

Martín Marconcini
A: 

Well perhaps I am misunderstanding generics,

But it seems a bit of a waste to have to write the same function overloaded for each type of integer, creating repeated code. A helper class seems a bit overkill.

Can anyone explain why this is an unsuitable use for generics?

Corin
+2  A: 

Unfortunately you are only able to specify struct in the where clause in this instance. It does seem strange you can't specify Int16, Int32, etc. specifically but I'm sure there's some deep implementation reason underlying the decision to not permit value types in a where clause.

I guess the only solution is to do a runtime check which unfortunately prevents the problem being picked up at compile time. That'd go something like:-

static bool IntegerFunction<T>(T value) where T : struct {
  if (typeof(T) != typeof(Int16)  &&
      typeof(T) != typeof(Int32)  &&
      typeof(T) != typeof(Int64)  &&
      typeof(T) != typeof(UInt16) &&
      typeof(T) != typeof(UInt32) &&
      typeof(T) != typeof(UInt64)) {
    throw new ArgumentException(
      string.Format("Type '{0}' is not valid.", typeof(T).ToString()));
  }

  // Rest of code...
}

Which is a little bit ugly I know, but at least provides the required constraints.

I'd also look into possible performance implications for this implementation, perhaps there's a faster way out there.

kronoz
A: 

I think you are misunderstanding generics. If the operation you are trying to perform is only good for specific data types then you are not doing something "generic".

Also, since you are only wanting to allow the function to work on int data types then you shouldn't need a separate function for each specific size. Simply taking a parameter in the largest specific type will allow the program to automatically upcast the smaller data types to it. (i.e. passing an Int16 will auto-convert to Int64 when calling).

If you are performing different operations based on the actual size of int being passed into the function then I would think you should seriously reconsider even trying to do what you are doing. If you have to fool the language you should think a bit more about what you are trying to accomplish rather than how to do what you want.

Failing all else, a parameter of type Object could be used and then you will have to check the type of the parameter and take appropriate action or throw an exception.

Tom Welch
Consider a class Histogram<T>. It makes sense to let it take a generic parameter, so the compiler can optimize it for bytes, ints, doubles, decimal, BigInt,... but at the same you need to prevent that you can create a, say, Histogram<Hashset>, because - speaking with Tron - it doesn't compute. (literally :))
Markus
A: 

Corin - it's not an unsuitable use for generics, what you want to do is something that it should support, but doesn't yet.

kronoz - that would work, but it would be relatively slow and you wouldn't really get the advantage.

What we need is something like:

static bool GenericMathFunction<T>(T value, T otherValue) 
    where T : operators( +, -, /, *, >, < ) 
{
    //this is just a dumb example to give the idea
    //a real equation could be much more complex
    return (value * (otherValue - value )) > (otherValue + value * value);
}

//another example, an extension to collections
static T Sum<T>( this IEnumerable<T> collection, T startingValue ) 
    where T : operators( + ) 
{
    T result = startingValue ;
    foreach( T item in collection );
        result += item;

    return result;
}

//this would then allow:
List<long> listOfLongs = new List<long> { 2, 3, 5, 7, 11, 13 .... }

long total = listOfLongs.Sum( 0 );

You just can't do this at the moment, even using kronoz's idea you wouldn't then have access to the Add/Multiply etc methods that you need on the numeric value types.

Keith
A: 

@Keith - I agree it's something which is currently unavailable but could be pretty useful, however I'm not sure whether there's some limitation in .net that is being expressed by the fact you can't specify a where clause with a list of value types as accepted types, it would seem strange not to provide that functionality when it is provided for reference types which makes me think it may be an underlying limitation in .net.

However methinks this could be a deep question which may require a visit to a blog such as Fabulous Adventures in Coding at http://blogs.msdn.com/ericlippert/default.aspx, and I don't know enough to say for sure (though I might go do some more research with ildasm, etc.)

kronoz
A: 

Tom:

While that is a good general solution, it is not suitable if I want to use UInt64 and Int64, i.e. either VERY large integers or large integers that can also be negative.

Corin
+4  A: 

What is the point of the exercise?

As people pointed out already, you could have a non-generic function taking the largest item, and compiler will automatically convert up smaller ints for you.

static bool IntegerFunction(Int64 value) { }

If your function is on performance-critical path (very unlikely, IMO), you could provide overloads for all needed functions.

static bool IntegerFunction(Int64 value) { }
...
static bool IntegerFunction(Int16 value) { }
dbkk
@dbkk: And then end up with significant code duplication.
Eric J.
+2  A: 

kronoz - Yeah, but I think everything they need is already part of .Net 3.5, it already supports duck-typing for IEnumerable and collection Add methods and keyword based constraints.

They have admitted that this is missing functionality when compared to C++ templates.

The underlying IL could support it, the C# syntax is what's lacking: http://weblogs.manas.com.ar/waj/2004/03/14/4/

Keith
+12  A: 

Hejlsberg has described the reasons for not implementing the feature in an interview with Bruce Eckel.

I have to admit, though, that I don't know how he thinks his proposed workaround will work. His proposal is to defer arithmetic operations to some other generic class (read the interview!). How does this help? IMHO, not much.

Konrad Rudolph
btw, MiscUtil provides a generic class that does exactly this; `Operator`/`Operator<T>`; http://www.yoda.arachsys.com/csharp/miscutil/usage/genericoperators.html
Marc Gravell
@Mark: good comment. However, just to be clear, I don't think that Hejlsberg was referring to code generation as a solution to the problem as you do in the `Operator<T>` code (since the interview was given long before the existence of the `Expressions` framework, even though one could of course use `Reflection.Emit`) – and I'd be *really* interested in *his* workaround.
Konrad Rudolph
A: 

This question is a bit of a FAQ one, so I'm posting this as wiki (since I've posted similar before, but this is an older one); anyway...

What version of .NET are you using? If you are using .NET 3.5, then I have a generic operators implementation in MiscUtil (free etc).

This has methods like T Add<T>(T x, T y), and other variants for arithmetic on different types (like DateTime + TimeSpan).

Additionally, this works for all the inbuilt, lifted and bespoke operators, and caches the delegate for performance.

Some additional background on why this is tricky is here.

You may also want to know that dynamic (4.0) sort-of solves this issue indirectly too - i.e.

dynamic x = ..., y = ...
dynamic result = x + y; // does what you expect
Marc Gravell
A: 

I would use a generic one which you could handle externaly...

/// <summary>
/// Generic object copy of the same type
/// </summary>
/// <typeparam name="T">The type of object to copy</typeparam>
/// <param name="ObjectSource">The source object to copy</param>
public T CopyObject<T>(T ObjectSource)
{
    T NewObject = System.Activator.CreateInstance<T>();

    foreach (PropertyInfo p in ObjectSource.GetType().GetProperties())
        NewObject.GetType().GetProperty(p.Name).SetValue(NewObject, p.GetValue(ObjectSource, null), null);

    return NewObject;
}
Marc Roussel