如何将泛型类限制为某些类型(包括本机数据类型)

时间:2014-06-29 17:28:30

标签: c# generics

我想创建一个通用结构(基本上是一个数组)并将可能的类型限制为ISerializable的类型和一堆本机数据类型,如int,uint,float,double,char等。问题是我无法标记这些具有接口的本机数据类型和我的研究表明,在泛型类型约束构造(where子句)中不可能使用类似关键字的东西。所以问题是我怎么能意识到这一点?

如果您对上下文感兴趣:我有一个BinaryStream类负责从/向流读取和写入。 自定义 ISerializable接口具有函数void Serialize(BinaryStream f),可以从f读取或写入f(取决于f中的状态)。实际上,写入或读取当然是构成结构的本机数据类型。这些是通过f.Transfer(ref data)读取或写入的。使用.NET框架中的标准BinarySerializer不是一种选择,因为它必须以自定义方式完成。

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]);
    }
}

2 个答案:

答案 0 :(得分:1)

您的研究已经向您提供了正确的信息 - .NET不允许您将泛型限制为某种抽象数字数据类型,因为它们(数值数据类型)没有任何公共接口。这是一个耻辱,有时是一个问题,但事实仍然如此。

如果你真的有非常通用的代码可以使用数字类型,那么你可以尝试用C ++ \ CLI实现你的类 - 它是C ++,所以它允许你使用模板,它是支持泛型的.NET语言:

// 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> {...};

然后,将创建的C ++ \ CLI dll添加为项目的引用。好吧,你仍然需要为每种数字类型创建这样的MyIntSerializer,但至少你不必复制所有的代码。为了有效地获得所需的IMySerializer<T>,您可以使用某些工厂通过属性或IsAssignableFrom通过反映搜索特定的IMySerializer<T>

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

这不是最简单的解决方案,但是我需要在计算中创建仅由数字类型不同的类型时使用。

修改

使用C ++ \ CLI是否有点过分?嗯,对于简单的情况,申请并不是很困难,但如果你不使用像

这样的东西
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
    }
 }

然后除了避免代码重复之外,这种方法没有真正的优点。并且,考虑到几乎没有数字类型,您可以在C#中实现特定的序列化:

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

并使用相同的工厂(或定位器)模式:

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

可以找到具体的实施者,如here

所述

答案 1 :(得分:0)

这是一个非常混乱的实现,但您如何看待将泛型类型保持为无约束并在构造函数中检查它?如果类型T不在允许类型的白名单中,它基本上会抛出NotImplementedException。