SFINAE equivalent in C#?

Started by
15 comments, last by swiftcoder 11 years, 6 months ago
I've just started mucking with C# generics, and it seems that I'm running headlong into their limitations. In C++ I can do something like this:
template <typename T>
void Write(T data) {
int size = T.SizeInBytes;
// ...
}

And I can call Write() with any type that contains an integer-compatible member variable named 'SizeInBytes'. If one tries to do the same in C#:
public void Write<T>(T data) where T : struct {
int size = T.SizeInBytes;
// ...
}

Then it errors out with "error CS0119: Expression denotes a `type parameter', where a `variable', `value' or `type' was expected". Quite reasonably, since C# doesn't allow the use of expressions with generic types which can't be applied solely given the constraints.

Are there any common idioms to working around this? Assuming that (a) I need to work with value types, and (b) these structs are defined in a 3rd party library (i.e. I can't modify the definition of the struct itself).

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

Advertisement
I believe Marshal.SizeOf and the typeof() operator should do what you need, it's been a while since I've used it though and I'm not sat in front of a computer I can test this on, but hopefully the following should work.


int size = Marshal.SizeOf(typeof(T));
Beautiful, thanks!

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

I've had similar problems a few times, usually it ends up as an unholy mess of generics and reflection.

I've had similar problems a few times, usually it ends up as an unholy mess of generics and reflection.

Ja, I'm quickly starting to miss C++ templates.

I'm going to have to unlearn my template-centric coding style for C#.

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]


Ja, I'm quickly starting to miss C++ templates.

I'm going to have to unlearn my template-centric coding style for C#.

There's a number of aspects of C++ templates that bug me. Stuff like this in particular:
[source lang="cpp"]void function(const std::vector&lt;Objects*&gt;&){}
void constFunction(const std::vector&lt;const Objects*&gt;&){}

std::vector&lt;Objects*&gt; objectList;
function(objectList);
constFunction(objectList); // compile error[/source]

Do C# generics let you do that sort of stuff easily?

[quote name='swiftcoder' timestamp='1348869994' post='4984880']
Ja, I'm quickly starting to miss C++ templates.

I'm going to have to unlearn my template-centric coding style for C#.

There's a number of aspects of C++ templates that bug me. Stuff like this in particular:
[source lang="cpp"]void function(const std::vector&lt;Objects*&gt;&){}
void constFunction(const std::vector&lt;const Objects*&gt;&){}

std::vector&lt;Objects*&gt; objectList;
function(objectList);
constFunction(objectList); // compile error[/source]

Do C# generics let you do that sort of stuff easily?
[/quote]

There is no such thing as "const" in C#, so that's a non issue.
However, it's normal that it doesn't work in c++. An array of const objects is not all all the same type as an array of non const objects. You can't expect implicit conversion of those two types. If you want to guarantee what I suspect is your wanted behavior, you should use a container where the element access function/operator returns const member if the container is const:

[source lang="cpp"]object* array &lt object* &gt ::operator[](int i);
const object* array &lt object* &gt ::operator[](int i) const;[/source]
with those two functions:
[source lang="cpp"]void function(array &lt objects* &gt &){}
void constFunction(const array &lt objects* &gt &){}[/source]
The relatively coarse-grained nature of generic constraints in C# / the CLI is my biggest annoyance with the platform. There's really no reason why the compiler couldn't infer method (and operator!) and property level constraints and simply require that all type arguments provide them.
Mike Popoloski | Journal | SlimDX

There is no such thing as "const" in C#, so that's a non issue.
However, it's normal that it doesn't work in c++. An array of const objects is not all all the same type as an array of non const objects. You can't expect implicit conversion of those two types.

I could expect it, given that the two objects should have both identical code and data, and one has an interface that is a subset of the other. The fact that it's so difficult to use one in place of the other is just a limitation imposed by poor design choices in the C++ template system.

If it C++ disallowed atrocities like this, the types would be compatible:
struct MyObject{};

template<class T> struct MyTemplateClass {
T x;
};

template<> struct MyTemplateClass<const MyObject*>{};

int main()
{
MyObject obj;

MyTemplateClass<MyObject*> i1;
i1.x= &obj;

MyTemplateClass<const MyObject*> i2;
i2.x= &obj; // compile error
}


Reading through stuff about C# generics, it seems that they maintain restrictions to prevent that sort of thing.

The relatively coarse-grained nature of generic constraints in C# / the CLI is my biggest annoyance with the platform. There's really no reason why the compiler couldn't infer method (and operator!) and property level constraints and simply require that all type arguments provide them.


As far as I can tell, the reason C# generics have the limitations they do is because they can be deployed in DLLs. This means that the compiled generic must work both for existing 'T' as well as unknown types that someone may cook up in the future (as long as those types fulfill all of the constraints the generic specifies). The compiler ensures that the generic can be used by anything that supports those constraints, without needing to actually be instantiated like C++ templates.

This is both their greatest strength and the cause of all of their weaknesses.

This topic is closed to new replies.

Advertisement