Reputation: 1728
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]);
}
}
Upvotes: 4
Views: 1710
Reputation: 1720
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.
Upvotes: -1
Reputation: 10401
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
Upvotes: 1