4

I want to create a generic structure (an array basically) and restrict the possible types to types of ISerializable and a bunch of native data types like int,uint,float,double,char etc. The problem is that I cannot mark these native data types with an interface and my researches stated that it is not possible to use something like an or keyword in generic type constraint construction (where clause). So the question is how can I realize that?

If you are interested in context: I have a BinaryStream class responsible for reading and writing from/to a stream. The custom ISerializable interface has function void Serialize(BinaryStream f) that either reads or write from/to stream f (depends on state in f). Actually written or read are of course native data types that the structs are made of. These are read or written via f.Transfer(ref data). Using a standard BinarySerializer from the .NET framework is not an option, because it has to be done in a custom way.

public class AutoArray<T> : ISerializable where T : ISerializable //or int or uint or float etc.
{
    private uint n;
    private T[] data;

    public void Serialize(BinaryStream f)
    {
        f.Transfer(ref n);
        for (int i = 0; i < n; i++)
            if (data[i] is ISerializable) data[i].Serialize(f);
            else f.Transfer(ref data[i]);
    }
}
T_D
  • 1,688
  • 1
  • 17
  • 28

2 Answers2

1

Your researches have yielded to you the correct information - .NET does not allow you to restrict generics to some abstract numeric data type, because they(numeric datatypes) do not have any common interface. That's a shame and sometimes a problem, but the fact nonetheless.

If you really have very generic code that could work with numeric types then you could try to implement your class in C++\CLI - it is C++, so it allows you to use templates, and it is .NET language that supports generics:

// C# - use base interface for your serializer, define it in separate assembly(or directly in C++\CLI)
public interface IMySerializer<T> {...}

// C++\CLI - add the reference to the project with IMySerializer<T>
template<class Type>
public ref abstract class MyNumericSerializerBase : IMySerializer<Type> {...};

// C++\CLI - you can't use template class in C# - it must be specialized
public ref class MyIntSerializer : MyNumericSerializerBase<Int32> {...};

Then you add the created C++\CLI dll as reference to your project. Well, you will still have to create such MyIntSerializer for each of the numeric types, but at least you won't have to replicate all the code. And to effectively get the needed IMySerializer<T> you can use some factory that searches for specific IMySerializer<T> through reflection either by attributes or through IsAssignableFrom:

// C#
public class MySerializerFactory
{
    public IMySerializer<T> GetSerializer<T>() {...}
}

It is not the simplest solution, but I used when I needed to create types that differ only by numeric types in calculations.

EDIT:

Could it be an overkill to use C++\CLI? Well, for simple cases it is not very difficult to apply, but if you won't use anything like

template<class Type>
public ref abstract class MyNumericSerializerBase : IMySerializer<Type> 
{ 
public:
    Type  GetValue(Type first, Type second)
    {
        return first + (second / 2); // The REAL advantage(and potential problems source) of C++\CLI templates
    }
 }

then there are no real advantages in such a method, except the avoidance of code duplication. And, taking into account that there are few numeric types you could just implement your specific serializers in C#:

// C#
public class MyInt32Serializer: IMySerializer<Int32> {...}

and use the same factory (or locator) pattern:

// C#
public class MySerializerFactory
{
    public IMySerializer<T> GetSerializer<T>() {...}
}

Concrete implementers could be found like it is described here

Community
  • 1
  • 1
Eugene Podskal
  • 10,270
  • 5
  • 31
  • 53
  • Well I would like to avoid to create a new dll because of such a minor feature. To use another language also seems pretty 'messy'. I already am using unsafe functions though, if this helps ^^ – T_D Jun 29 '14 at 17:59
  • The closed generic/factory pattern is what C# is encouraging you to do here. – Preston Guillot Jun 29 '14 at 22:59
-1

This is a tremendously messy implementation, but what would you think of leaving the generic type unconstrained and checking it in the constructor? Basically have it throw a NotImplementedException if type T isn't on your whitelist of allowed types.

algorowara
  • 1,700
  • 1
  • 15
  • 15
  • Yeah, I also considered that and it is a possible solution with the disadvantage of losing compile time error detection. You say it is super messy. I am also open for a better implementation. So if you or someone can enlighten me :) – T_D Jun 29 '14 at 18:57
  • Sorry, clarification: I meant my proposed solution was messy. I didn't mean to impugn your solution/attempt. – algorowara Jun 29 '14 at 18:59