如何添加类型约束以在泛型方法中包含任何可序列化的内容?

Posted

技术标签:

【中文标题】如何添加类型约束以在泛型方法中包含任何可序列化的内容?【英文标题】:How can I add a type constraint to include anything serializable in a generic method? 【发布时间】:2010-10-31 00:53:52 【问题描述】:

我的通用方法需要序列化传递给它的对象,但是仅仅坚持它实现 ISerializable 似乎不起作用。例如,我有一个从 Web 服务返回的结构(标有 SerializableAttribute),它可以很好地序列化为 xml,但正如预期的那样,C# 编译器会报错。

有没有一种方法可以在尝试序列化对象之前检查对象是否可序列化,或者更好的是,使用where 关键字来检查对象是否合适?

这是我的完整方法:

public static void Push<T>(string url, T message)
        where T : ISerializable

    string xml = SerializeMessage(message);

    // Send the message to Amazon SQS
    SendMessageRequest sendReq = new SendMessageRequest  QueueUrl = url, MessageBody = xml ;
    AmazonSQSClient client = new AmazonSQSClient(S3User, S3Pass);
    client.SendMessage(sendReq);

和 SerializeMessage:

private static string SerializeMessage<T>(T message)

    XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
    using (StringWriter stringWriter = new StringWriter())
    
        xmlSerializer.Serialize(stringWriter, message);
        return stringWriter.ToString();
    

如果这不可能,那么在运行时检查对象是否可序列化的最佳方法是什么?

【问题讨论】:

【参考方案1】:

您不能完全通过通用约束来做到这一点,但您可以做一些事情来提供帮助:

1) 将 new() 约束放在泛型类型上(以启用反序列化功能并确保 XmlSerializer 不会抱怨缺少默认 ctor):

where T : new()

2) 在处理序列化的方法的第一行(或构造函数或其他任何您不必一遍又一遍地重复它的地方),您可以执行以下检查:

if( !typeof(T).IsSerializable && !(typeof(ISerializable).IsAssignableFrom(typeof(T)) ) )
    throw new InvalidOperationException("A serializable Type is required");

当然,尝试序列化类型时仍有可能出现运行时异常,但这将涵盖最明显的问题。

【讨论】:

这个答案不正确,因为它会给出误报。 typeof(T) 计算结果为 System.Runtime.Type,它是 ISerializable,因此它总是计算结果为 true。改用这个... if (!typeof(T).IsSerializable && !(typeof(ISerializable).IsAssignableFrom(typeof(T)))) 其中 T : new() 不包括标有 Serializable 属性的 System.Tuple。 没错,但这似乎仍然很安全。大多数人甚至不知道元组是什么,更不用说有一个 .NET Tuple 类。另外,Tuple 无论如何都只会使用二进制序列化(没有 XML)进行序列化,因为它没有默认的 ctor。【参考方案2】:

我写了一篇关于这个主题的长篇博客文章,您可能会觉得有帮助。它主要涉及二进制序列化,但概念适用于大多数任何序列化格式。

http://blogs.msdn.com/jaredpar/archive/2009/03/31/is-it-serializable.aspx

它的长短是

无法添加可靠的通用约束 检查对象是否可序列化的唯一方法是对其进行序列化并查看操作是否成功

【讨论】:

【参考方案3】:

了解对象是否可序列化的唯一方法是尝试对其进行序列化。

实际上,您是在问如何判断一个类型是否“可序列化”,但实际问题是关于对象的。即使类型标记为 [Serializable],某些类型的实例也可能无法序列化。例如,如果实例包含循环引用怎么办?

【讨论】:

【参考方案4】:

代替

XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));

试试

XmlSerializer xmlSerializer = new XmlSerializer(message.GetType());

【讨论】:

【参考方案5】:

C# 8 及更高版本允许unmanaged 约束将类型限制为仅包含值类型的结构(在任何嵌套级别上)。我们真正想要的是:

public class MyClass<T> where T : ISerializable or unmanaged

但不幸的是,在编写 C# 时不支持这种语法(约束总是 AND,用逗号分隔)。

一种解决方法可能是 ValueWrapper 类:

public class ValueWrapper<U> : ISerializable where U : unmanaged

这需要一个 U 作为构造函数参数。它有一个属性U Value。现在您可以将值类型视为 ISerializable,只需将它们包装在 ValueWrapper 中即可。

【讨论】:

以上是关于如何添加类型约束以在泛型方法中包含任何可序列化的内容?的主要内容,如果未能解决你的问题,请参考以下文章

无法在泛型方法中将类型更改为可为空

涉及泛型对象的泛型属性的赋值无法在泛型函数中正确进行类型检查

泛型编程的术语

LayaBox---TypeScript---泛型

C#学习笔记8

在泛型方法中返回原始集合类型