我知道,就几种分布式技术(如RPC)而言,使用术语"编组"但不理解它与序列化的区别.它们不是都将对象转换为一系列位吗?
什么是序列化?
什么是对象编组?
编组和序列化在远程过程调用的上下文中是松散的同义词,但在语义上是不同的.
特别是,编组是关于从这里到那里获取参数,而序列化是关于将结构化数据复制到诸如字节流之类的基本形式或从基本形式复制结构化数据.从这个意义上讲,序列化是执行编组的一种方法,通常实现按值传递语义.
对象也可以通过引用进行编组,在这种情况下,"在线上"的数据只是原始对象的位置信息.但是,这样的对象可能仍然适合于序列化.
正如@Bill所提到的,可能还有其他元数据,例如代码库位置甚至是对象实现代码.
两者都有一个共同点 - 即序列化对象.序列化用于传输对象或存储它们.但:
序列化:序列化对象时,只将该对象中的成员数据写入字节流; 而不是实际实现对象的代码.
编组: 当我们讨论将Object传递给远程对象(RMI)时使用术语编组.在编组对象中序列化(成员数据被序列化)+附加了代码库.
因此序列化是编组的一部分.
CodeBase是告诉Object的接收者可以找到该对象的实现的信息.任何认为它可能将对象传递给之前可能没有看到它的另一个程序的程序必须设置代码库,以便接收方可以知道从哪里下载代码,如果它没有本地可用的代码.在对对象进行反序列化时,接收器将从中获取代码库并从该位置加载代码.
来自编组(计算机科学)维基百科的文章:
术语"marshal"被认为与Python标准库1中的"serialize"同义,但这些术语在Java相关RFC 2713中不是同义词:
"编组"对象意味着以这样的方式记录其状态和代码库:当编组对象被"解组"时,可以通过自动加载对象的类定义来获得原始对象的副本.您可以封送任何可序列化或远程的对象.编组就像序列化一样,除了编组还记录代码库.编组与序列化的不同之处在于编组处理特殊的远程对象.(RFC 2713)
"序列化"对象意味着将其状态转换为字节流,使得字节流可以转换回对象的副本.
因此,编组还会在字节流中保存对象的代码以及其状态.
我认为主要区别在于编组据说还涉及代码库.换句话说,您将无法将对象编组和解组为另一个类的状态等效实例..
序列化只是意味着您可以存储对象并重新获得等效状态,即使它是另一个类的实例.
话虽如此,它们通常是同义词.
封送处理是指将函数的签名和参数转换为单字节数组. 专门用于RPC的目的.
序列化通常是指将整个对象/对象树转换为字节数组 .Marshaling将序列化对象参数,以便将它们添加到消息中并将其传递到网络中. *序列化也可用于存储到磁盘.*
编组是告诉编译器如何在另一个环境/系统上表示数据的规则; 例如;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] public string cFileName; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)] public string cAlternateFileName;
因为您可以看到两个不同的字符串值表示为不同的值类型.
序列化只会转换对象内容,而不是表示(将保持相同)并遵守序列化规则,(导出或不导出).例如,私有值不会被序列化,公共值为yes,对象结构将保持不变.
这是两者的更具体的示例:
序列化示例:
#include#include #include 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#include 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#include #include #include 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); }
在封送处理中,数据不必一定要展平,但需要将其转换为其他替代表示形式。所有铸件都是封送,但并非所有封送都是铸件。
封送处理不需要涉及动态分配,它也可以只是结构之间的转换。例如,您可能有一对,但该函数希望该对的第一个和第二个元素处于相反的位置;例如,您将一对配对强制转换/存储将无法完成任务,因为fst和snd会被翻转。
#includetypedef 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; }
当您开始处理多种类型的带标记的联合时,封送处理的概念变得尤为重要。例如,您可能很难找到一个JavaScript引擎来为您打印“ c字符串”,但是您可以要求它为您打印一个包装的c字符串。或者,如果您想从Lua或Python运行时中的JavaScript运行时中打印字符串。它们都是弦乐器,但经常不经过封送就无法相处。
我最近的一个烦恼是JScript数组以“ __ComObject”的形式编组到C#,并且没有使用该对象的文档记录方式。我可以找到它的地址,但是我真的对它一无所知,所以真正弄清楚它的唯一方法是以任何可能的方式戳它,并希望找到关于它的有用信息。因此,使用诸如Scripting.Dictionary之类的友好接口创建新对象,将数据从JScript数组对象复制到其中,然后将该对象传递给C#而不是JScript的默认数组,变得更加容易。
test.js:
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); } } }
上面打印的是“ __ComObject”,从C#的角度来看,它有点像一个黑匣子。
另一个有趣的概念是,您可能了解如何编写代码,以及一台知道如何执行指令的计算机,因此,作为程序员,您正在有效地编排您希望计算机从大脑到程序执行的概念。图片。如果我们有足够好的编组员,我们可以考虑一下我们想做/更改的事情,程序将以这种方式更改而无需在键盘上键入。因此,如果您有办法在真正想写分号的地方存储大脑中所有物理变化几秒钟,则可以将该数据编组为信号以打印分号,但这是一个极端。