在远程过程调用的背景下,Marshaling和Serialization是松散的同义词,但从意图上看它们有语义上的不同。
特别地,Marshaling是关于如何将参数从这里传递到那里,而Serialization是关于将结构化数据复制到或从原始形式(例如字节流)中。在这个意义上,Serialization通常实现值传递语义,是执行Marshaling的一种方式之一。
同时,一个对象也可以通过引用来被Marshaled,此时“在线”上的数据仅仅是原始对象的位置信息。然而,这样的对象可能仍然适用于值Serialization。
正如@Bill提到的那样,还可能存在其他元数据,例如代码库位置甚至对象实现代码。
这两者都有一个共同点 - 也就是将对象进行序列化。序列化用于对象的传输或存储。但是:
因此,序列化是编组的一部分。
CodeBase是提供接收者该对象实现位置信息的信息。任何认为可能会将对象传递给之前未见过该对象的另一个程序的程序必须设置代码库,以便接收者可以知道从哪里下载代码(如果没有本地可用的代码)。接收者将在对对象进行反序列化后从中获取代码库并从该位置加载代码。
invokeAndWait
和Forms的Invoke
,它们将同步调用编组到UI线程,而无需涉及序列化。 - Jeffrey Hantin从Marshalling (computer science)维基百科文章中:
在Python标准库中,“marshal”这个术语被认为是与“serialize”同义的1,但是在与Java相关的RFC 2713中,这两个术语并不是同义词:
对于一个对象,“marshal”的意思是记录它的状态和代码库,使得当“unmarshal”这个已编组的对象时,可以通过自动加载对象的类定义来获得原始对象的副本。您可以编组任何可序列化或远程的对象。编组类似于序列化,只是编组还记录了代码库。“Marshal”和序列化不同之处在于它特别处理远程对象。(RFC 2713)
“Serialize”一个对象意味着将其状态转换为字节流,以便可以将字节流转换回对象的副本。
所以,编组还会将对象的代码库保存在字节流中,除了它的状态。
字节流 - 流是一系列数据。输入流 - 从源读取数据。输出流 - 将数据写入目的地。 Java字节流用于逐字节(每次8位)执行输入/输出操作。字节流适用于处理原始数据,如二进制文件。 Java字符流用于逐个读取/写入2个字节,因为在Java中使用Unicode约定存储字符,每个字符需要2个字节。字符流在处理(读取/写入)文本文件时很有用。
RMI(远程方法调用) - 是提供在Java中创建分布式应用程序机制的API。RMI允许对象在另一个JVM中运行时调用该对象上的方法。
序列化和编组都可以松散地用作同义词。以下是一些区别。
序列化 - 对象的数据成员被写入二进制形式或字节流(然后可以写入文件/内存/数据库等)。一旦对象数据成员被写入二进制形式,就无法保留关于数据类型的信息。
Marshalling(编组)是将对象序列化(以二进制格式转换为字节流),并附加数据类型和代码库,然后传递给远程对象(RMI)的过程。Marshalling会将数据类型转换为预定命名规则,以便可以根据初始数据类型重构它。
因此,序列化是Marshalling的一个部分。
CodeBase(代码库)是一条信息,告诉接收Object的程序这个对象的实现可在何处找到。任何可能向另一个之前未见过该对象的程序传递对象的程序都必须设置代码库,以便接收者可以知道从哪里下载代码(如果没有本地可用的代码)。接收方将在反序列化对象时从中获取代码库,并从该位置加载代码。(摘自@Nasir的回答)
Serialization(序列化)几乎就像是对象使用的内存的“愚蠢”内存转储,而Marshalling(编组)存储有关自定义数据类型的信息。
在某种程度上,序列化执行按值传递的实现的Marshalling,因为没有传递数据类型的信息,只有原始形式被传递到字节流中。
如果数据流从一个操作系统传输到另一个操作系统,由于不同的操作系统可能使用不同的方式表示同一数据,因此序列化可能涉及大端和小端等问题。另一方面,尽管结果是更高级别的表示形式,但编组在不同操作系统之间迁移时完全没有问题。
我认为主要的区别在于Marshalling(编组)据说还涉及代码库。换句话说,您不能将对象编组和解组为不同类的状态等效实例。
序列化仅意味着您可以存储对象并重新获取等效状态,即使它是另一个类的实例。
话虽如此,它们通常是同义词。
Marshaling是将函数的签名和参数转换为单个字节数组的过程。 这是专门为了RPC而进行的。
Serialization更常指将整个对象/对象树转换成字节数组。 Marshaling将序列化对象参数,以便将它们添加到消息中并通过网络传递。*Serialization也可用于存储到磁盘中.*
Marshalling是告诉编译器如何在另一个环境/系统上表示数据的规则;例如:
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string cFileName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
public string cAlternateFileName;
正如您所看到的,两个不同的字符串值表示为不同的值类型。
序列化仅会转换对象的内容,而不是表示(将保持不变),并遵守序列化规则(导出什么或不导出)。例如,私有值将不会被序列化,公共值会被序列化,并且对象结构将保持不变。
以下是更具体的两个例子:
序列化示例:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
typedef struct {
char value[11];
} SerializedInt32;
SerializedInt32 SerializeInt32(int32_t x)
{
SerializedInt32 result;
itoa(x, result.value, 10);
return result;
}
int32_t DeserializeInt32(SerializedInt32 x)
{
int32_t result;
result = atoi(x.value);
return result;
}
int main(int argc, char **argv)
{
int x;
SerializedInt32 data;
int32_t result;
x = -268435455;
data = SerializeInt32(x);
result = DeserializeInt32(data);
printf("x = %s.\n", data.value);
return result;
}
MarshalDemoLib.cpp
)#include <iostream>
#include <string>
extern "C"
__declspec(dllexport)
void *StdCoutStdString(void *s)
{
std::string *str = (std::string *)s;
std::cout << *str;
}
extern "C"
__declspec(dllexport)
void *MarshalCStringToStdString(char *s)
{
std::string *str(new std::string(s));
std::cout << "string was successfully constructed.\n";
return str;
}
extern "C"
__declspec(dllexport)
void DestroyStdString(void *s)
{
std::string *str((std::string *)s);
delete str;
std::cout << "string was successfully destroyed.\n";
}
(MarshalDemo.c
)
#include <Windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
int main(int argc, char **argv)
{
void *myStdString;
LoadLibrary("MarshalDemoLib");
myStdString = ((void *(*)(char *))GetProcAddress (
GetModuleHandleA("MarshalDemoLib"),
"MarshalCStringToStdString"
))("Hello, World!\n");
((void (*)(void *))GetProcAddress (
GetModuleHandleA("MarshalDemoLib"),
"StdCoutStdString"
))(myStdString);
((void (*)(void *))GetProcAddress (
GetModuleHandleA("MarshalDemoLib"),
"DestroyStdString"
))(myStdString);
}
在编组中,数据不一定需要被压平,但需要转换为另一种替代表示形式。所有类型转换都是编组,但并非所有编组都是类型转换。
编组不需要涉及动态分配,它也可以只是结构之间的转换。例如,您可能拥有一个 pair,但函数期望 pair 的第一个和第二个元素相反;您将一个 pair 转换到另一个 pair(使用 casting/memcpy)将不能完成工作,因为 fst 和 snd 会翻转。
#include <stdio.h>
typedef struct {
int fst;
int snd;
} pair1;
typedef struct {
int snd;
int fst;
} pair2;
void pair2_dump(pair2 p)
{
printf("%d %d\n", p.fst, p.snd);
}
pair2 marshal_pair1_to_pair2(pair1 p)
{
pair2 result;
result.fst = p.fst;
result.snd = p.snd;
return result;
}
pair1 given = {3, 7};
int main(int argc, char **argv)
{
pair2_dump(marshal_pair1_to_pair2(given));
return 0;
}
var x = new ActiveXObject('Dmitry.YetAnotherTestObject.YetAnotherTestObject');
x.send([1, 2, 3, 4]);
(YetAnotherTestObject.cs
)
using System;
using System.Runtime.InteropServices;
namespace Dmitry.YetAnotherTestObject
{
[Guid("C612BD9B-74E0-4176-AAB8-C53EB24C2B29"), ComVisible(true)]
public class YetAnotherTestObject
{
public void send(object x)
{
System.Console.WriteLine(x.GetType().Name);
}
}
}
我对编组的理解与其他答案不同。
序列化:
利用某种约定,生成或恢复对象图的线格式版本。
编组:
利用映射文件生成或还原对象图的线格式版本,以便结果可以自定义。该工具可能开始遵循一项约定,但重要的区别在于能够自定义结果。
基于契约的开发:
在基于契约的开发环境中,编组非常重要。
水化一个对象是指获取一个存在于内存中但尚未包含任何领域数据(“真实”数据)的对象,然后用领域数据(例如来自数据库、网络或文件系统)填充它。
- pawel-schmidtMarshalling通常在相对紧密相关的进程之间进行;而序列化并不一定具有这种期望。因此,当在进程之间进行数据马歇尔操作时,您可能只希望发送一个指向可能昂贵的数据的引用以便恢复,而在序列化时,您将希望保存所有数据,以便在反序列化时正确地重新创建对象。
I
、更改大小写等。 - Jeffrey Hantin