C #'s yield return returns non-serializable IEnumerable and IEnumerator
In. NET, most common IEnumerable and IEnumerator can be serialized (with the Serializable feature ). For example, Array, Dictionary <K, V>, HashSet <T>, List <T>, sorted List, Queue <T>, Stack <T>, String, ResourceSet ...... And so on. Generally, the GetEnumerator method of the serializable IEnumerable will return an identical serializable iterator (IEnumerator ).
Note that a one-dimensional Array starting with 0 will return Array. SZArrayEnumerator (the GetEnumerator of IEnumerable <T> shows that System is returned when the interface is executed. SZArrayHelper. SZGenericArrayEnumerator <T> iterator ). Other types of arrays return the Array. ArrayEnumerator iterator. Both are serializable.
However, the IEnumerable and IEnumerator generated by yield return cannot be serialized, which is very important! Especially when the application domain (AppDomain) program data interaction is expanded, data must either be serializable or inherit from MarshalByRefObject, that is, value-based and reference-based messages.
It can be verified through a simple program:
Static void Main ()
{
Console. WriteLine (able1 (). GetType ());
Console. WriteLine (able2 (). GetType ());
Console. WriteLine (tor1 (). GetType ());
Console. WriteLine (tor2 (). GetType ());
}
Static IEnumerable <int> able1 ()
{
For (int I = 0; I <5; I ++)
{
Yield return I + 1;
}
}
Static IEnumerable <int> able2 ()
{
Return new int [] {1, 2, 3, 4, 5 };
}
Static IEnumerator <int> tor1 ()
{
For (int I = 0; I <5; I ++)
{
Yield return I + 1;
}
}
Static IEnumerator <int> tor2 ()
{
Return (IEnumerable <int>) new int [] {1, 2, 3, 4, 5}). GetEnumerator ();
}
Output:
Mgen. Program + <able1> d _ 0
System. Int32 []
Mgen. Program + <tor1> d _ 4
System. SZArrayHelper + SZGenericArrayEnumerator '1 [System. Int32]
The second and fourth classes are both serializable, and the objects returned with yield return are automatically generated by the compiler and are not serializable. Below are two class definitions under Reflector:
[CompilerGenerated]
Private sealed class <able1> d _ 0: IEnumerable <int>, IEnumerable, IEnumerator <int>, IEnumerator, IDisposable
[CompilerGenerated]
Private sealed class <tor1> d _ 4: IEnumerator <int>, IEnumerator, IDisposable
As mentioned above, IEnumerable and IEnumerator cannot be sent across application domains. This article is also due to the following problems:
Class Program: MarshalByRefObject
{
Static void Main ()
{
// Create an application domain
AppDomain appDomain = AppDomain. CreateDomain ("new appdomain ");
// Create a Program object in another application domain
Program pro = (Program) appDomain. CreateInstanceAndUnwrap (Assembly. GetExecutingAssembly (). FullName,
Typeof (Program). FullName );
// Exception: the returned value must be serializable!
IEnumerator <int> iter = pro. test ();
}
IEnumerator <int> test ()
{
For (int I = 0; I <5; I ++)
{
Yield return I + 1;
}
}
}
The solution is of course not to use yield return or customize its own serializable iterator.
Of course, yield return is quite necessary in some cases. Of course, it can be used to make a simple packaging of non-serializable objects generated by the compiler yield return across application domains.
The complete code is as follows:
Using System;
Using System. Collections. Generic;
Using System. Linq;
Using System. Collections;
Using System. Runtime. Remoting;
Using System. Reflection;
Namespace Mgen. TTC
{
Class Program: MarshalByRefObject
{
Static void Main ()
{
// Create an application domain
AppDomain appDomain = AppDomain. CreateDomain ("new appdomain ");
// Create a Program object in another application domain
Program pro = (Program) appDomain. CreateInstanceAndUnwrap (Assembly. GetExecutingAssembly (). FullName,
Typeof (Program). FullName );
IEnumerator <int> iter = pro. test ();
While (iter. MoveNext ())
Console. WriteLine (iter. Current );
Console. WriteLine ("Whether the proxy is transparent: {0}", RemotingServices. IsTransparentProxy (iter ));
}
IEnumerator <int> internal_test ()
{
For (int I = 0; I <5; I ++)
{
Yield return I + 1;
}
}
IEnumerator <int> test ()
{
Return new MyEnumerator <int> (internal_test ());
}
}
Class MyEnumerator <T>: MarshalByRefObject, IEnumerator <T>
{
IEnumerator <T> iter;
Public MyEnumerator (IEnumerator <T> I)
{
Iter = I;
}
Public T Current
{
Get {return iter. Current ;}
}
Public void Dispose ()
{
Iter. Dispose ();
}
Object IEnumerator. Current
{
Get {return (IEnumerator) iter). Current ;}
}
Public bool MoveNext ()
{
Return iter. MoveNext ();
}
Public void Reset ()
{
Iter. Reset ();
}
}
}
Output:
1
2
3
4
5
Whether it is a transparent Proxy: True
OK. The iterator can be used and belongs to a transparent proxy (in another application domain ).