15

is there a where clause for a generic that determines that T is of type primitive?

 void Method<T>(T val) where T : primitive

case:

have a functional language written in C that feeds on primitive unmanaged blitable types, or things that can be pushed into a primitive easily (eg. a date without hours/mins/seconds could be pushed to an int, etc.) The original plan was to utilise GPU. Not doing that though. C# is holding up well in its co-ordinator role so far. I tend to think of the schema's home as living in C#. This isn't strictly true, but the idea serves the project well.

I like OO, but when it comes to functional ideas, I'd like to constrain those thoughts to types that are supported in that domain. Interestingly, I'm still leaning on C# to help me stay structured and disciplined. I don't see that changing.


There are other reasons why getting more detailed with constraints would be a good thing for me.

btw: resharper suggested explicit interfaces which I tried for a while. I really liked this notation... and the constraints can live with the interface too. Nice. However, I came across Jon Skeet's warning on S/O about this messing up inheritance. So, back to a more labour intensive run-time AssertIsXYZ.

To take that a little further, where constraints to me are a step towards proof of correctness (old ideas, but still good ones). The typing system seems to enable some of this to be pushed to the compiler. Using the word "where" made think about a clause or phrase (like in SQL/LINQ). I'm not asking for it to be taken to the nth degree. The more work the compiler can do the better as far as I'm concerned.

Getting tactile with constraints helped me clarify some ideas. Got to give credit there... but it's a pity that I had to comment the constraints out afterwards.

sgtz
  • 8,849
  • 9
  • 51
  • 91

7 Answers7

20

I suspect that what you want based on your comments on Jon's answer is a way to constrain a type parameter to either blittable types or unmanaged types.

An "unmanaged type" is a type whose definition precludes any reference to memory tracked by the garbage collector; you can only make pointer types out of unmanaged types. Blittable types are those types which can be marshalled from managed to unmanaged code without any modification to their bits; they are a subset of the unmanaged types.

A number of people have told us that it would be quite handy to have a generic constraint that constrains a type parameter to be only an unmanaged type. We have experimented with prototypes of C# and the CLR that have this constraint, but have no plans at this time to actually put the feature into the product. If you can describe the scenario that is motivating the feature request, that would help us prioritize the feature against the hundreds of other features that are also possible.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • 1
    with that constraint could we do a sizeof(T) and get a jit time constant value? The only time this would be complex would be if you had embeddeded IntPtr and if you did the onus is on the coder to know what they are doing then anyway. Likewise could you then do reinterpret casts in c# to any other compile time known unmanged type (by a similar constriant or otherwise). That would eliminate the need for some annoying C++/CLI hoops we have to jump through to efficiently work with enums, or blit arbitrary generic types we hope (read, runtime check, not compile check) are unmanaged – ShuggyCoUk Sep 27 '11 at 16:07
  • 1
    @ShuggyCoUk: Correct. In our prototype, one of the features we had was that "sizeof(T)" worked the way you wanted on a type parameter known to be of blittable type, you could make a "pointer to T" for a generic type parameter T, and so on. – Eric Lippert Sep 27 '11 at 16:46
  • That would be genuinely useful in that it would avoid the annoyance of dropping to C++/CLI and prevent compile time check errors. Of course now we have the CPP utility functions ticking along nicely it's less of a big deal in terms of direct utility if I was directly looking to use that sort of thing. Perhaps being able to do a stackalloc for them would be useful, but I've found preallocated scratch buffers passed around and pinned in leaf nodes of the call graph actually end up being pretty good. – ShuggyCoUk Sep 27 '11 at 17:10
  • Ultimately it would have made a bunch of code I wrote (to deal with extremelevels of direct binary compatibility of an over the wire protocol that allowed overwriting of sections of the message via simple indexes into the buffer) quite a lot less verbose. Being able to just cast in a few places rather than some slightly less pleasant functions returning longs. Overall it's pretty extreme end of the spectrum, I would think anyone that cared went to C++/CLI for it already. Oh it would also make writing some hash function composition much much simpler too. – ShuggyCoUk Sep 27 '11 at 17:14
  • +1 @Eric Lippert ;-) ... provide a few cases for you. In a nutshell, sometimes it's really good to have the compiler as the first unit test (comment stollen from a recent Scot Hanselman podcast). – sgtz Sep 27 '11 at 20:23
  • `static T LoadData(string key) { var value = IsolatedStorageSettings.ApplicationSettings[key]; return typeof(T).IsPrimitive ? (T)value : value as T; }`. This code fails because T cannot be used with the `as` operator. Using `where T : class` on a hand does not allow me to declare the counterpart `where T : !class`. – Cœur Sep 11 '14 at 11:55
  • A place where this generic type constraint would be **really useful** would be anything that wants to (edit: *more safely*) use the generic methods on `System.Runtime.CompilerServices.Unsafe`. – Joe Amenta May 05 '17 at 12:11
  • @EricLippert Is bool really a blittable type? [This document](https://learn.microsoft.com/en-us/dotnet/framework/interop/blittable-and-non-blittable-types) states that it is not. Or am I confusing things, that document defines blittable as something to do with Marshaling but you speak of it more in terms of the GC. – Alex Telon Oct 12 '17 at 12:04
  • 1
    @AlexTelon: No, I'm confused; I had not realized bool was not blittable. I'll fix the text. – Eric Lippert Oct 12 '17 at 17:03
12

There's

where T : struct

That's not the same as it being a primitive type, mind you. It forces it to be a non-nullable value type. That would include, say, Guid (which isn't a primitive) but exclude Nullable<Guid> (which isn't a primitive either, but also isn't a class).

If you can be more precise about your requirements, we may be able to help you more.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • just that it is likely that T would need to be blitable i.e. be representable as a native type. It's to do with some calcs, and defining T this way would clarify intention. I can live without it I guess. Would where T : struct include int/short/bool/float/single/etc. – sgtz Sep 26 '11 at 06:58
  • @sgtz: Those are all non-nullable value types, so would be included, yes. – Jon Skeet Sep 26 '11 at 07:03
7

No! There is no constraint for primitive type.

Your best bet in enforcing primitive type is as under:

void Method<T>(T val) where T:struct
{
    if (!typeof(T).IsPrimitive)
        throw new ArgumentException("Only primitive types are allowed.", "val");
}

OR

public void Method(int val)
{
    GenericMethod(val);
}

public void Method(double val)
{
    GenericMethod(val);
}

private void GenericMethod<T>(T val) where T:struct
{
}
Muhammad Hasan Khan
  • 34,648
  • 16
  • 88
  • 131
3

I am just appending some new information to this thread, sorry for digging it up...

As many people have already answered, the following construct can be used for this reason for quite some time now:

void Method<T>(T val) where T : struct

With the release of C# 7.3, one will be able to write:

void Method<T>(T val) where T : unmanaged

which insures that you can take a pointer/address/size of T, meaning that the following is now perfectly valid:

unsafe static U BinaryConvert<T, U>(T input)
    where T : unmanaged
    where U : unmanaged => *(U*)&input;


float value = 42.80f;
int bits = BinaryConvert<float, int>(value);

This does technically help you in strictly constraining T to language primitives ... but it's a start, I guess.


Sources:

unknown6656
  • 2,765
  • 2
  • 36
  • 52
2

You can't do this(at least currently). If you want it to be a value type:

void Method<T>(T val) where T : struct

or you can check it at run time to make sure it's really primitive(I think you want int/float/etc instead of all value types?)

public static class Ext
{
    public static bool IsPrimitive(this Type t)
    {
        return t == typeof(int) || t == typeof(float) 
            || t == typeof(double) || ...
    }
}

EDIT LOL just found that there is a built-in property in Type named IsPrimitive, that's what I'm going to say...

Cheng Chen
  • 42,509
  • 16
  • 113
  • 174
0

There is not, but you can use struct constraint.

where T: struct
NaveenBhat
  • 3,248
  • 4
  • 35
  • 48
0

A constraint has to be a non-sealed type, so there isn't a way to constrain a generic to a primitive type.

David Anderson
  • 13,558
  • 5
  • 50
  • 76