C# generics: cast generic type to value type

23,767

Solution 1

First off, this is a really bad code smell. Any time you're doing a type test on a type parameter like this odds are good you're abusing generics.

The C# compiler knows that you are abusing generics in this way and disallows the cast from the value of type T to int, etc. You can turn off the compiler getting in your way by casting the value to object before you cast it to int:

return BitConverter.GetBytes((int)(object)this._value);

Yuck. Again, it would be better to find another way to do this. For example:

public class NumericValue
{
    double value;
    enum SerializationType { Int, UInt, Double, Float };
    SerializationType serializationType;        

    public void SetValue(int value)
    {
        this.value = value;
        this.serializationType = SerializationType.Int
    }
    ... etc ...

    public byte[] GetBytes()
    {
        switch(this.serializationType)
        {
            case SerializationType.Int:
                return BitConverter.GetBytes((int)this.value);
            ... etc ...

No generics necessary. Reserve generics for situations that are actually generic. If you've written the code four times one for each kind of type, you haven't gained anything with generics.

Solution 2

Late to the party, but just wanted to comment on the comments saying that the original proposal was a "bad design" - in my opinion, the original proposal (though it doesn't work) was not "necessarily" a bad design at all!

Coming from a strong C++ (03/11/14) background with deep understanding of template meta-programming, I created a type generic serialization library in C++11 with minimal code repetition (the goal is to have non-repetitive code, and I believe I have achieved 99% of it). The compile time template meta-programming facilities as provided by C++11, though can become extremely complex, helps achieve true type generic implementation of the serialization library.

However, it is very unfortunate that when I wanted to implement a simpler serialization framework in C#, I was stuck exactly on the problem that the OP had posted. In C++, the template type T can be totally "forwarded" to the site of usage, while C# generics does not forward the actual compile time type to the usage site - any second (or more) level reference to a generic type T makes T becoming a distinct type that is not usable at all at the actual usage site, thus GetBytes(T) cannot determine that it should invoke a specific typed overload - worse, there is even no nice way in C# to say: hey, I know T is int, and if the compiler doesn't know it, does "(int)T" make it an int?

Also, instead of blaming that type based switch has a smell of bad design - this has been a great misnomer that whenever people are doing some advanced type based framework and has to resort to type based switch due to inability of the language environment, without really understanding the constraints of the actual problem at hand, people starts to blatantly say type based switch is a bad design - it is, for most of the traditional OOP usage cases, but there are special cases, most of the time advanced usage case like the problem we are talking here, that this is necessary.

It is also worth mentioning that I would actually blame that the BitConverter class is designed in a traditional and incompetent way to suit generic needs: instead of defining a type specific method for each type with regard to "GetBytes", maybe it would be more generic friendly to define a generic version of GetBytes(T value) - possibly with some constraints, so the user generic type T can be forwarded and work as expected without any type switch at all! The same is true for all the ToBool/ToXxx methods - if the .NET framework provides the facilities as non-generic version, how would one expect a generic framework trying to utilize this foundation framework - type switch or if without type switch, you end up duplicating the code logic for each data type you are trying to serialize - Oh, I miss the day I worked with C++ TMP that I only write the serialization logic once for practically unlimited number of types I can support.

Solution 3

Well, it strikes me that the type really isn't properly generic to start with: it can only be one of a few types, and you can't express that constraint.

Then you want to call a different overload of GetBytes based on the type of T. Generics doesn't work well for that sort of thing. You could use dynamic typing to achieve it, in .NET 4 and above:

public byte[] GetBytes()
{
    return BitConverter.GetBytes((dynamic) _value);
}

... but again this doesn't really feel like a nice design.

Solution 4

Pretty late answer, but anyways... there is a way to make it slightly nicer... Make use of generics in a this way: Implement another generic type which converts the types for you. So you don't have to care about unboxing, casting etc of the type to object... it will just work.

Also, in your GenericClass, now you don't have to switch the types, you can just use IValueConverter<T> and also cast it as IValueConverter<T>. This way, generics will do the magic for you to find the correct interface implementation, and in addition, the object will be null if T is something you do not support...

interface IValueConverter<T> where T : struct
{
    byte[] FromValue(T value);
}

class ValueConverter:
    IValueConverter<int>,
    IValueConverter<double>,
    IValueConverter<float>
{
    byte[] IValueConverter<int>.FromValue(int value)
    {
        return BitConverter.GetBytes(value);
    }

    byte[] IValueConverter<double>.FromValue(double value)
    {
        return BitConverter.GetBytes(value);
    }

    byte[] IValueConverter<float>.FromValue(float value)
    {
        return BitConverter.GetBytes(value);
    }
}

public class GenericClass<T> where T : struct
{
    T _value;

    IValueConverter<T> converter = new ValueConverter() as IValueConverter<T>;

    public void SetValue(T value)
    {
        this._value = value;
    }

    public byte[] GetBytes()
    {
        if (converter == null)
        {
            throw new InvalidOperationException("Unsuported type");
        }

        return converter.FromValue(this._value);
    }
}

Solution 5

You could potentially use Convert.ToInt32(this._value) or (int)((object)this._value). But in general if you find yourself having to check for specific types in a generic method, there's a problem with your design.

In your case, you probably should consider making an abstract base class, and then derived classes for the types you're going to use:

public abstract class GenericClass<T>
where T : struct
{
    protected T _value;

    public void SetValue(T value)
    {
        this._value = value;
    }

    public abstract byte[] GetBytes();
}

public class IntGenericClass: GenericClass<int>
{
    public override byte[] GetBytes()
    {
        return BitConverter.GetBytes(this._value);
    }
}
Share:
23,767
rittergig
Author by

rittergig

Updated on July 27, 2022

Comments

  • rittergig
    rittergig almost 2 years

    I have a generic class which saves value for the specified type T. The value can be an int, uint, double or float. Now I want to get the bytes of the value to encode it into an specific protocol. Therefore I want to use the method BitConverter.GetBytes() but unfortunately Bitconverter does not support generic types or undefined objects. That is why I want to cast the value and call the specific overload of GetBytes(). My Question: How can I cast a generic value to int, double or float? This doesn't work:

    public class GenericClass<T>
        where T : struct
    {
        T _value;
    
        public void SetValue(T value)
        {
            this._value = value;
        }
    
        public byte[] GetBytes()
        {
            //int x = (int)this._value;
            if(typeof(T) == typeof(int))
            {
                return BitConverter.GetBytes((int)this._value);
            }
            else if (typeof(T) == typeof(double))
            {
                return BitConverter.GetBytes((double)this._value);
            }
            else if (typeof(T) == typeof(float))
            {
                return BitConverter.GetBytes((float)this._value);
            }
        }
    }
    

    Is there a possibility to cast an generic value? Or is there another way to get the bytes?

  • rittergig
    rittergig about 11 years
    My goal is to write a class to save a 2d matrix (2d array). The class has methods to add rows and columns. I handle the data in an List<List<TYPE>> field so I can easily extend the number of rows and columns whithout always creating new 2d array. Furthermore the class should have an method to export the data to a blow (big byte array where the bytes of each field is saved. Therefore I need the BitConverter. But I don't want to write double code. Why there is not GetBytes<T>() method?
  • Eric Lippert
    Eric Lippert about 11 years
    @rittergig: Consider writing an immutable matrix for doing math operations. You don't mutate the number 2 into 5 when you add 3 to it; you create a brand-new number 5 and the 2 still exists. The same can be true of matrices.
  • Serge Pavlov
    Serge Pavlov over 3 years
    Agreed this is an interesting issue in "no nice way in C# to say: hey, I know T is int...". The solution is Unsafe.As() library method. While it works, it is still "unsafe" for a reason, so needs to be used with discretion.
  • Jeremy Lakeman
    Jeremy Lakeman about 3 years
    converter can be static. I would also consider static Action<T,byte[]> convert = ... with a static initialiser that locates the BitConverter.GetBytes method with a matching argument type. Not sure if that would actually reduce runtime overhead though.
  • tcwicks
    tcwicks about 2 years
    @MichaC Your solution is the highest performance solution that I've found so far. Thanks for this :)