關於MarshalByRefObject的解釋
首先了解一下不同應用程序域中的對象的通信方式有兩種:
一種是跨應用程序域邊界傳輸對象副本
一種是使用代理交換消息。
簡單來講,繼承此類的對象可以跨越應用程序域邊界被引用,甚至被遠程引用.
當一個對象需要長途跋涉到另一個環境中時,需要將其marshal成一個可以傳輸的形態(比如在.NET Remoting中對象將被打包成一個serializable的ObjRef實例——這個ByRef就是指ObjRef這種形態);同理,當打包以后傳輸到目標地點,還要執行unmarshal的操作將其還原為內存中的對象
你實際上只擁有對這個對象的一個遠程引用,雖然你可以調用它的方法,但實際上這些操作都是發生在遠程的(就是前面講過的過程)
別外解釋一下WebService,
Web services 使用 XML 來編解碼數據,並使用 SOAP(通信協議)來傳輸數據。大家可以發現WebService代理類的MarshalByRefObject對象的。
remoting 是MarshByReference的,可以傳變量的引用,直接對服務器對象操作。速度快,適合intranet(企業內部互聯網)。
webservice 是MarshByValue的,必須傳對象的值。速度慢,可以過FIREWALL,配置比較簡單,適合internet(因特網)。
概括的說Remoting與Web Services的區別是:
(1)既支持TCP信道又支持HTTP信道,傳輸速度快
(2)即可傳輸XML的SOAP包又可傳輸二進制流,效率高
(3)Remoteing主要用於C/S結構項目
(4)不一定要依賴IIS服務器
一般來說,remoting是和平台相關的,需要客戶和服務器都是.NET,但可配置特性比較好,可以自定義協議。web service可以做到跨平台通信,但必須采用SOAP協議。
出處:https://blog.csdn.net/weixin_33907511/article/details/93344855
=======================================================================================
C# MarshalByRefObject 和Serializable的區別
這兩種方式的類一般都是用於遠程傳輸時使用。
marshalbyrefobject是通過引用傳遞serializable是通過值傳遞,現在就來分析下什么是引用傳遞,什么是值傳遞。
理解這個對Remoting或者webservice的認識是很重要的。
marshalbyrefobject(引用)本機或者是服務器上的其實都是同一個實例,只不過是服務器創建后你在本地使用了那個對象而已。比如說A類繼承了marshalbyrefobject那么A類由服務器創建實例了,客戶端都可以使用這個實例了。
現在我們假設A類有一個方法叫着A,Function返回值為一個string類型這個方法有一系列的操作。客戶端在調用這個方法的時候只得到服務器返回的一個值,那個一系列的操作都將在服務器完成,這就是所謂的餿客服端。
Serializable(值類型)這個就不同了,假定我們剛才的那個A類的Funciton方法需要一個B類作為參數,B是一個可序列化的類,也就是類的定義上面加了[Serializable()],如果沒加那么這個方法將會報錯。我們通過一個remoting的例子來解釋一下
先寫一個繼承marshalbyrefobject的類
public class HelloServer : MarshalByRefObject
{
public HelloServer()
{ Console.WriteLine("HelloServer activated"); }
public String HelloUserMethod(User user)
{
string title;
if (user.Male)
title = "先生";
else
title = "女士";
Console.WriteLine( "Server Hello.HelloMethod : 你好,{0}{1}", user.Name,title);
return "你好," + user.Name + title;
}
}
再寫一個可序列化的類
[Serializable]
public class User
{
public User(string name,bool male)
{
this.name = name;
this.male = male;
}
string name="";
bool male=true;
public string Name
{
get{return name;}
set{name = value;}
}
public bool Male
{
get{return male;}
set{male = value;}
}
}
現在我們將在服務端和客戶端使用它們。
服務端如下:
public class Server
{
public static int Main(string [] args)
{
TcpChannel chan1 = new TcpChannel(8085);
HttpChannel chan2 = new HttpChannel(8086);
ChannelServices.RegisterChannel(chan1);
ChannelServices.RegisterChannel(chan2);
RemotingConfiguration.RegisterWellKnownServiceType (typeof(HelloServer), "SayHello", WellKnownObjectMode.Singleton); //創建類的實例
System.Console.WriteLine("Press Enter key to exit");
System.Console.ReadLine();
return 0;
}
客戶端如下:
public class Client
{
public static void Main(string[] args)
{
//使用HTTP通道得到遠程對象
HttpChannel chan2 = new HttpChannel();
ChannelServices.RegisterChannel(chan2);
HelloServer obj1 = (HelloServer)Activator.GetObject(
typeof(RemotingSamples.HelloServer),
"http://localhost:8086/SayHello");//創建類的實例
if (obj1 == null)
{
System.Console.WriteLine(
"Could not locate HTTP server");
}
Console.WriteLine(
"Client1 TCP HelloUserMethod {0}",
obj1.HelloUserMethod(new User("張生",true))); //將類作為參數
(將User作為參數必須是serializable) }
}
}
出處:https://blog.csdn.net/CNHK1225/article/details/50351433
=======================================================================================
MarshalByRefObject 陷阱
近日在做一個項目,涉及到跨 AppDomain 的處理,在使用 MarshalByRefObject 對象時遇到並解決了一些“詭異”的問題,故在此總結一下,與大家分享,也留日后備忘。
跨域傳遞數據時,只有兩類數據可以傳遞:可序列化類型、MarshalByRefObject 派生類型。前者包括基本類型、使用了 SerializeAttribute 的類型、實現了ISerializable 接口的類型。在跨域傳遞數據時,可序列化類型的數據將通過序列化傳遞數據副本;MarshalByRefObject 派生類型則只是在請求數據AppDomain創建一個指向目標AppDomain中真實對象的透明代理。

2 {
3 Console.WriteLine( " 當前域:{0} " , AppDomain.CurrentDomain.FriendlyName);
4 AppDomain domain = AppDomain.CreateDomain( " RemoteDomain " );
5
6 domain.DoCallBack( delegate ()
7 {
8 // 在遠程域創建數據對象;
9 AppDomain.CurrentDomain.SetData( " DT " , new DataObject());
10 });
11
12 // 獲取遠程域中創建的數據對象;
13 DataObject dt = (DataObject)domain.GetData( " DT " ); // dt實際是 System.Runtime.Remoting.Proxies.__TransparentProxy
14 dt.DoSomething();
15
16 Console.ReadLine();
17 }
18 }
19
20 public class DataObject : MarshalByRefObject
21 {
22 public void DoSomething()
23 {
24 Console.WriteLine( " 當前域:{0} " , AppDomain.CurrentDomain.FriendlyName);
25 }
26 }
以上的規則看起來似乎很簡單,進行跨域傳遞對象似乎也沒什么特別的地方。但如果真的認為毫無特別之處,那你就真的錯了。下面我把我曾犯過的一些錯誤:
- 錯誤1:清空上面代碼中對創建的 DataObject 的所有引用,再執行 GC.Collect() 操作強制執行垃圾回收,將會銷毀創建的 DataObject 對象。
實際上並不會產生預期的 DataObject 在執行 GC.Collect() 之后立即被回收的效果。我們將上面的代碼修改如下:

2 {
3 public static void Main( string [] args)
4 {
5 Console.WriteLine( " 當前域:{0} " , AppDomain.CurrentDomain.FriendlyName);
6 AppDomain domain = AppDomain.CreateDomain( " RemoteDomain " );
7
8 domain.DoCallBack( delegate ()
9 {
10 // 在遠程域創建數據對象;
11 AppDomain.CurrentDomain.SetData( " DT " , new DataObject());
12 });
13
14 // 獲取遠程域中創建的數據對象;
15 DataObject dt = (DataObject)domain.GetData( " DT " ); // dt實際是 System.Runtime.Remoting.Proxies.__TransparentProxy
16 dt.DoSomething();
17
18 domain.SetData( " DT " , null );
19 dt = null ;
20 GC.Collect();
21
22 Console.ReadLine();
23 }
24 }
25
26 public class DataObject : MarshalByRefObject
27 {
28 public DataObject()
29 {
30 Console.WriteLine( " 創建了 DataOjbect…… 當前域:{0} " , AppDomain.CurrentDomain.FriendlyName);
31 }
32
33 ~ DataObject()
34 {
35 Console.WriteLine( " 銷毀 DataObject ……當前域:{0} " , AppDomain.CurrentDomain.FriendlyName);
36 }
37
38 public void DoSomething()
39 {
40 Console.WriteLine( " 當前域:{0} " , AppDomain.CurrentDomain.FriendlyName);
41 }
42 }
除非你立即執行 AppDomain.Unload(domain); 卸載遠程域,才會立即銷毀 DataObject,此時能夠看到析構函數中的輸出。
為什么呢? DataObject 似乎已經沒有任何引用了,難道不是嗎?這實際上還有引用:domain 中進行跨域通訊的 Remoting 基礎結構在持有 DataObject ,該結構是負責將請求數據的遠程AppDomain 中的透明代理的調用請求轉發給真實的對象的,MarshalByRefOjbect 通過生命期服務 LifeService 控制該結構的生命周期。DataObject 對象之所以未被銷毀是因為該基礎結構的生命周期尚未過期。默認情況下,如果不再有任何調用操作后大約15分鍾將銷毀該結構,DataOjbect 才可以被垃圾回收。對生命周期的控制請參考 MSDN 關於.NET Remoting 的生命期服務相關內容。
- 錯誤2:通過透明代理訪問一個基本類型的屬性(如 int)或者可序列化的對象類型的屬性不會產生對真實對象所在的遠程域的遠程調用。

2 {
3 // ID;
4 public int _id;
5 /// <summary>
6 /// ID;
7 /// 讀寫屬性;
8 /// </summary>
9 public int ID
10 {
11 get { return _id; }
12 set { _id = value; }
13 }
14
15 // data1;
16 private InnerData1 _data1;
17 /// <summary>
18 /// data1;
19 /// 讀寫屬性;
20 /// </summary>
21 public InnerData1 Data1
22 {
23 get { return _data1; }
24 set { _data1 = value; }
25 }
26
27 // data2;
28 private InnerData2 _data2;
29 /// <summary>
30 /// data2;
31 /// 讀寫屬性;
32 /// </summary>
33 public InnerData2 Data2
34 {
35 get { return _data2; }
36 set { _data2 = value; }
37 }
38
39
40 public DataObject()
41 {
42 Console.WriteLine( " 創建了 DataOjbect…… 當前域:{0} " , AppDomain.CurrentDomain.FriendlyName);
43 }
44
45 ~ DataObject()
46 {
47 Console.WriteLine( " 銷毀 DataObject ……當前域:{0} " , AppDomain.CurrentDomain.FriendlyName);
48 }
49
50 public void DoSomething()
51 {
52 Console.WriteLine( " 當前域:{0} " , AppDomain.CurrentDomain.FriendlyName);
53 }
54 }
55
56 [Serializable]
57 public class InnerData1
58 {
59 public string Data { get ; set ; }
60 }
61
62 public class InnerData2 : MarshalByRefObject
63 {
64 public string Data { get ; set ; }
65 }
這也是一個陷阱。實際上,無論屬性的類型是什么,通過透明代理對對象的任何訪問,包括通過 GET/SET 訪問器訪問屬性、訪問公開的成員、調用方法等,都會產生跨域調用,即使屬性/成員的類型是可序列化的,透明代理也不會保存其屬性值的副本,如上面的修改后的 DataObject 的所有的屬性,包括 public int _id; 成員。運行以上的測試代碼很容易驗證,不做詳細敘述了。
知曉這一點所具備的指導意義是:當頻繁訪問一個大數據量而不變化的序列化類型的屬性時,更好的做法是一次訪問后在本地做緩存,而不要直接通過透明代理訪問屬性,因為跨域調用時頻繁地對大數據進行序列化和反序列化將會消耗你寶貴的計算資源。
跨域操作表面簡單,而底層實際是 .NET Remoting 實現的,這其中的機制是比較復雜的。透明代理雖然給我們帶來了巨大的便利,屏蔽了大量的復雜的底層細節的同時,也帶來了讓人容易在不了解底層機制的情形下誤用而產生難以察覺的 BUG 的風險。當你需要進行跨 AppDomain 操作是,深入了解 .NET Remoting 機制對於提升系統的健壯性是很重要的。
出處:https://www.cnblogs.com/haiq/articles/1758060.html
=======================================================================================
對MarshalByRefObject的解釋
問:
打擾一下,請問MarshalByRefObject中的"Marshal"應該怎樣理解?
回復:
按照package的意思理解——當一個對象需要長途跋涉到另一個環境中時,需要將其marshal成一個可以傳輸的形態(比如在.NET Remoting中對象將被打包成一個serializable的ObjRef實例——這個ByRef就是指ObjRef這種形態);同理,當打包以后傳輸到目標地點,還要執行unmarshal的操作將其還原為內存中的對象。:)
問:
謝謝!
MarshalByRefObject是不是可以這樣理解:對被引用的對象進行Marshal。如果按照package的意思理解,那package的過程是怎樣的?
MSDN上這樣講:
MarshalByRefObject是通過使用代理交換消息來跨應用程序域邊界進行通訊的對象的基類.
MarshalByRefObject對象在本地應用程序域的邊界內可直接訪問。遠程應用程序域中的應用程序首次訪問MarshalByRefObject時,會向該遠程應用程序傳遞代理。對該代理后面的調用將封送回駐留在本地應用程序域中的對象。
在Marshal中,上面所說的代理是什么?有什么用?
MSDN上還講到:
當跨應用程序域邊界使用類型時,類型必須是從MarshalByRefObject繼承的,而且由於對象的成員在創建它們的應用程序域之外無法使用,所以不得復制對象的狀態。
既然對象的狀態不能傳遞過去,那傳遞這個對象又有何意義?
第一次去理解MarshalByRefObject,有的問題可能提的比較膚淺,請您指點。
回復:
MarshalByRefObject是所有可以在AppDomain邊界外部訪問的對象的基類,重心不是marshal,而是object,即object that could be marshaled by reference,也就是可以通過Ref(實際上是ObjRef對象)的機制進行“封送”(MSDN中文版對marshal一詞的翻譯)的對象。封送的行為是由代理來做的,這里說的代理就是我文章中講過的.NET Remoting的真實代理(即RemotingProxy)。真實代理不是有一個Invoke()方法嗎?當你透過對一個MBRO的透明代理訪問該對象的方法時,透明代理將把基於堆棧的方法調用轉換為方法調用消息(IMethodCallMessage)並轉發給真實代理(在Remoting的場合中也即RemotingProxy),而RemotingProxy的任務就是把對象封送並連同方法調用消息一起轉發給遠程應用程序域;到達目的地以后的操作類似:遠程應用程序域中的監聽方當收到發來的方法調用消息時,先取出封送好的ObjRef(這個對象里面保存着發來調用的那個對象!),將其結封(unmarshal)為本地的對象,並獲得其透明代理,然后就可以把方法調用消息在轉換回基於堆棧的調用發送給這個對象。
對象是在本地維護的,但是方法可以在遠程調用。你比如說一個web應用程序,你是通過本地的瀏覽器遠程訪問這個應用程序,但是應用程序的狀態不會由你的瀏覽器負責(所以你只是在訪問這個應用程序提供給你的功能而已,你並沒於擁有應用程序本身,包括其所有數據),你只是發送一個個的請求,服務器告訴你處理的結果。在Remoting中 也是一樣,當你獲得一個遠程對象的時候,你實際上只擁有對這個對象的一個遠程引用,雖然你可以調用它的方法,但實際上這些操作都是發生在遠程的(就是前面 講過的過程),你只是傳入了一些參數,得到了一個結果,但對象的狀態還是在遠程維護的(換句話說,對象本身也就是對象的所有狀態並沒有被往返傳遞,傳遞的 只是傳入傳出的參數——當然,如果參數是一個MBRO的話,還是傳遞對象被封送的引用)。
也許應該給你准備一個好理解的例子……你就會豁然開朗了。:)
問:
我這樣的理解對不對?
一般的對象與從MarshalByRefObject繼承的對象區別是:
一般的對象只能在本地應用程序域之內被引用,而MarshalByRefObject對象可以跨越應用程序域邊界被引用,甚至被遠程引用。
回復:
Exactly! 當對象跨出AppDomain邊界的時候,實際上只是它的一個引用(ObjRef)。你比如說吧:
public class LocalObject
{
public void CallRemoteObject(MarshalByRefObject mbro)
{
Console.WriteLine(mbro.ToString());
}
}
當傳入一個在本地創建的 mbro 對象時, ToString() 方法是直接發送給對象的;而當 mbro 是通過 Remoting 創建的遠程對象的話,實際上它只是一個包含有已經 marshal 好的 ObjRef 的透明代理, ObjRef 里面有什么?對象實例的 URI !所以當你調用這個遠程對象時,相當於向這個遠程端口( tcp://remoteServer/xxxx.rem )發送方法調用消息而已。只不過透明代理隱藏了對象位置的概念,而 RemotingProxy 真實代理則是實際上處理遠程方法調用和對象封送的中樞對象。
出處:http://www.blogjava.net/killme2008/archive/2007/03/28/107037.html
=======================================================================================