我们可以将代表保存在一个文件(C#)中

我有一个有代表团成员的class级。 我可以为该类的每个实例化对象设置委托,但尚未find任何方法来保存该对象

这是一个相当危险的事情。

虽然可以像其他任何对象一样序列化和反序列化委托,但委托是指向程序内部序列化的方法的指针。 如果你在另一个程序中反序列化对象,你会得到一个SerializationException – 如果你幸运的话。

例如,让我们稍微修改darin的程序:

 class Program { [Serializable] public class Foo { public Func<string> Del; } static void Main(string[] args) { Func<string> a = (() => "a"); Func<string> b = (() => "b"); Foo foo = new Foo(); foo.Del = a; WriteFoo(foo); Foo bar = ReadFoo(); Console.WriteLine(bar.Del()); Console.ReadKey(); } public static void WriteFoo(Foo foo) { BinaryFormatter formatter = new BinaryFormatter(); using (var stream = new FileStream("test.bin", FileMode.Create, FileAccess.Write, FileShare.None)) { formatter.Serialize(stream, foo); } } public static Foo ReadFoo() { Foo foo; BinaryFormatter formatter = new BinaryFormatter(); using (var stream = new FileStream("test.bin", FileMode.Open, FileAccess.Read, FileShare.Read)) { foo = (Foo)formatter.Deserialize(stream); } return foo; } } 

运行它,你会看到它创build对象,序列化它,将它反序列化成一个新的对象,当你在新对象上调用Del时,它返回“a”。 优秀。 好的,现在注释掉对WriteFoo的调用,以便程序只是反序列化对象。 再次运行该程序,并获得相同的结果。

现在交换a和b的声明并运行程序。 让人惊讶。 现在反序列化的对象正在返回“b”。

这是因为实际被序列化的是编译器分配给lambdaexpression式的名称。 编译器按照发现它们的顺序将名称分配给lambdaexpression式。

这就是这样的风险:你没有序列化委托,你正在序列化一个符号。 这是符号的价值 ,而不是符号代表什么,被序列化。 反序列化对象的行为取决于该符号的值在反序列化它的程序中表示的值。

在一定程度上,所有的系列化都是如此。 将对象反序列化为实现对象类的程序,而不是序列化程序所做的,乐趣就开始了。 但是序列化委托将序列化的对象耦合到序列化它的程序的符号表,而不是对象的类的实现。

如果是我,我会考虑明确这个耦合。 我会创build一个Foo的静态属性,它是一个Dictionary<string, Func<string>> ,用键和函数填充它,并且存储每个实例中的键而不是函数。 这使得反序列化程序负责在开始反序列化Foo对象之前填充字典。 在某种程度上,这与使用BinaryFormatter序列化委托所做的完全一样; 不同之处在于,这种方法使得反序列化程序的function赋予符号的责任更加明显。

其实你可以用BinaryFormatter保存types信息。 这里是certificate:

 class Program { [Serializable] public class Foo { public Func<string> Del; } static void Main(string[] args) { Foo foo = new Foo(); foo.Del = Test; BinaryFormatter formatter = new BinaryFormatter(); using (var stream = new FileStream("test.bin", FileMode.Create, FileAccess.Write, FileShare.None)) { formatter.Serialize(stream, foo); } using (var stream = new FileStream("test.bin", FileMode.Open, FileAccess.Read, FileShare.Read)) { foo = (Foo)formatter.Deserialize(stream); Console.WriteLine(foo.Del()); } } public static string Test() { return "test"; } } 

如果您决定使用BinaryFormatter,您应该注意的一个重要事项是其格式没有很好的文档logging,并且实现可能会在.NET和/或CLR版本之间发生重大变化。

一个委托是一个方法指针,当你说保存的时候我可能会误解,但是如果你尝试保存和恢复地址,那么在运行时添加到委托的位置可能不再存在。

所以,这是我的理解,你想“保存”一个函数指针(委托)。 现在,如果将所有委托函数放入库中,则可以使用系统reflection来在运行时构build链接,然后可以select将委托转换为编译器定义的委托(同样在库中)。 唯一的缺点是目标方法必须是一个明确定义的位置,所以没有匿名方法,因为在每次编译时,在编译时定义了位置。 下面是我能够在运行时重新创build委托的代码,使用时风险自负,而且没有备注。

更新:你可以做的另一件事是创build一个自定义属性,并将其应用到你想要创build到委托中的任何和所有方法。 在运行时,使用系统reflection,遍历find的导出types,然后从具有自定义属性的types中select所有的方法。 这可能不仅仅是你想要的,只有在你也提供了一个“ID”值时才有用,所以有一个逻辑的方式,通过一个主查找表将id链接到所需的委托。

我也刚刚注意到,由于风险因素,你放弃了这个方法的评论,我将在这里留下来提供另一种做事的方式。

  using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.Serialization; using System.Reflection; namespace RD.Runtime { [Serializable] public struct RuntimeDelegate { private static class RuntimeDelegateUtility { public static BindingFlags GetSuggestedBindingsForMethod(MethodInfo method) { BindingFlags SuggestedBinding = BindingFlags.Default; if (method.IsStatic) SuggestedBinding |= BindingFlags.Static; else SuggestedBinding |= BindingFlags.Instance; if (method.IsPublic) SuggestedBinding |= BindingFlags.Public; else SuggestedBinding |= BindingFlags.NonPublic; return SuggestedBinding; } public static Delegate Create(RuntimeDelegate link, Object linkObject) { AssemblyName ObjectAssemblyName = null; AssemblyName DelegateAssemblyName = null; Assembly ObjectAssembly = null; Assembly DelegateAssembly = null; Type ObjectType = null; Type DelegateType = null; MethodInfo TargetMethodInformation = null; #region Get Assembly Names ObjectAssemblyName = GetAssemblyName(link.ObjectSource); DelegateAssemblyName = GetAssemblyName(link.DelegateSource); #endregion #region Load Assemblys ObjectAssembly = LoadAssembly(ObjectAssemblyName); DelegateAssembly = LoadAssembly(DelegateAssemblyName); #endregion #region Get Object Types ObjectType = GetTypeFromAssembly(link.ObjectFullName, ObjectAssembly); DelegateType = GetTypeFromAssembly(link.DelegateFullName, DelegateAssembly); #endregion #region Get Method TargetMethodInformation = ObjectType.GetMethod(link.ObjectMethodName, link.SuggestedBinding); #endregion #region Create Delegate return CreateDelegateFrom(linkObject, ObjectType, DelegateType, TargetMethodInformation); #endregion } private static AssemblyName GetAssemblyName(string source) { return GetAssemblyName(source, source.ToUpper().EndsWith(".DLL") || source.ToUpper().EndsWith(".EXE")); } private static AssemblyName GetAssemblyName(string source, bool isFile) { AssemblyName asmName = null; try { if (isFile) asmName = GetAssemblyNameFromFile(source); else asmName = GetAssemblyNameFromQualifiedName(source); } catch (Exception err) { string ErrorFormatString = "Invalid Call to utility method 'GetAssemblyNameOrThrowException'\n" + "Arguments passed in:\n" + "=> Source:\n[{0}]\n" + "=> isFile = {1}\n" + "See inner exception(s) for more detail."; throw new InvalidOperationException(string.Format(ErrorFormatString, source, isFile), err); } if (asmName == null) throw new InvalidOperationException(asmName.Name + " Assembly Name object is null, but no other error was encountered!"); return asmName; } private static AssemblyName GetAssemblyNameFromFile(string file) { #region Validate parameters if (string.IsNullOrWhiteSpace(file)) throw new ArgumentNullException("file", "given a null or empty string for a file name and path"); if (!System.IO.File.Exists(file)) throw new ArgumentException("File does not exsits", "file"); #endregion AssemblyName AssemblyNameFromFile = null; try { AssemblyNameFromFile = AssemblyName.GetAssemblyName(file); } catch (Exception err) { throw err; } return AssemblyNameFromFile; } private static AssemblyName GetAssemblyNameFromQualifiedName(string qualifiedAssemblyName) { #region Validate parameters if (string.IsNullOrWhiteSpace(qualifiedAssemblyName)) throw new ArgumentNullException("qualifiedAssemblyName", "given a null or empty string for a qualified assembly name"); #endregion AssemblyName AssemblyNameFromQualifiedAssemblyName = null; try { AssemblyNameFromQualifiedAssemblyName = new AssemblyName(qualifiedAssemblyName); } catch (Exception err) { throw err; } return AssemblyNameFromQualifiedAssemblyName; } private static Assembly LoadAssembly(AssemblyName assemblyName) { Assembly asm = LoadAssemblyIntoCurrentAppDomain(assemblyName); if (asm == null) throw new InvalidOperationException(assemblyName.Name + " Assembly is null after loading but no other error was encountered!"); return asm; } private static Assembly LoadAssemblyIntoCurrentAppDomain(AssemblyName assemblyName) { #region Validation if (assemblyName == null) throw new ArgumentNullException("assemblyName", "Assembly name is null, must be valid Assembly Name Object"); #endregion return LoadAssemblyIntoAppDomain(assemblyName, AppDomain.CurrentDomain); } private static Assembly LoadAssemblyIntoAppDomain(AssemblyName assemblyName, AppDomain appDomain) { #region Validation if (assemblyName == null) throw new ArgumentNullException("assemblyName", "Assembly name is null, must be valid Assembly Name Object"); if (appDomain == null) throw new ArgumentNullException("appDomain", "Application Domain is null, must be a valid App Domain Object"); #endregion return appDomain.Load(assemblyName); } private static Type GetTypeFromAssembly(string targetType, Assembly inAssembly) { #region Validate if (string.IsNullOrWhiteSpace(targetType)) throw new ArgumentNullException("targetType", "Type name is null, empty, or whitespace, should be type's display name."); if (inAssembly == null) throw new ArgumentNullException("inAssembly", "Assembly is null, should be valid assembly"); #endregion try { return inAssembly.GetType(targetType, true); } catch (Exception err) { string ErrorFormatMessage = "Unable to retrive type[{0}] from assembly [{1}], see inner exception."; throw new InvalidOperationException(string.Format(ErrorFormatMessage, targetType, inAssembly), err); } } private static Delegate CreateDelegateFrom(Object linkObject, Type ObjectType, Type DelegateType, MethodInfo TargetMethodInformation) { if (TargetMethodInformation.IsStatic & linkObject == null) { return CreateStaticMethodDelegate(DelegateType, TargetMethodInformation); } if (linkObject != null) { ValidateLinkObjectType(linkObject, ObjectType); } else { linkObject = CreateInstanceOfType(ObjectType, null); } return CreateInstanceMethodDelegate(linkObject, DelegateType, TargetMethodInformation); } private static Delegate CreateStaticMethodDelegate(Type DelegateType, MethodInfo TargetMethodInformation) { return Delegate.CreateDelegate(DelegateType, TargetMethodInformation); } private static void ValidateLinkObjectType(object linkObject, Type ObjectType) { if (!ObjectType.IsInstanceOfType(linkObject)) { throw new ArgumentException( string.Format("linkObject({0}) is not of type {1}", linkObject.GetType().Name, ObjectType.Name), "linkObject", new InvalidCastException( string.Format("Unable to cast object type {0} to object type {1}", linkObject.GetType().AssemblyQualifiedName, ObjectType.AssemblyQualifiedName), new NotSupportedException( "Conversions from one delegate object to another is not support with this version" ) ) ); } } private static Object CreateInstanceOfType(Type targetType, params Object[] parameters) { #region Validate if (targetType == null) throw new ArgumentNullException("targetType", "Target Type is null, must be valid System type."); #endregion try { return System.Activator.CreateInstance(targetType, parameters); } catch (Exception err) { string ErrorFormatMessage = "Invalid call to CreateInstanceOfType({0}, Object[])\n" + "parameters found:\n" + "{1}" + "See inner exception for further information."; string ParamaterInformationLine = GetParamaterLine(parameters); throw new NotSupportedException( string.Format(ErrorFormatMessage, targetType.Name, ParamaterInformationLine), err); } } private static string GetParamaterLine(Object[] parameters) { if (parameters == null) return "NONE\n"; string ParamaterFormatLine = "==> Paramater Type is {0} and object is {1}\n"; string ParamaterInformationLine = string.Empty; foreach (object item in parameters) { ParamaterInformationLine += string.Format(ParamaterFormatLine, item.GetType().Name, item); } return ParamaterInformationLine; } private static Delegate CreateInstanceMethodDelegate(Object linkObject, Type DelegateType, MethodInfo TargetMethodInformation) { return Delegate.CreateDelegate(DelegateType, linkObject, TargetMethodInformation); } } public string ObjectSource; public string ObjectFullName; public string ObjectMethodName; public string DelegateSource; public string DelegateFullName; public BindingFlags SuggestedBinding; public RuntimeDelegate(Delegate target) : this(target.Method.DeclaringType.Assembly.FullName, target.Method.DeclaringType.FullName, target.Method.Name, target.GetType().Assembly.FullName, target.GetType().FullName, RuntimeDelegateUtility.GetSuggestedBindingsForMethod(target.Method)) { } public RuntimeDelegate( string objectSource, string objectFullName, string objectMethodName, string delegateSource, string delegateFullName, BindingFlags suggestedBinding) :this() { #region Validate Arguments if (string.IsNullOrWhiteSpace(objectSource)) throw new ArgumentNullException("ObjectSource"); if (string.IsNullOrWhiteSpace(objectFullName)) throw new ArgumentNullException("ObjectFullName"); if (string.IsNullOrWhiteSpace(objectMethodName)) throw new ArgumentNullException("ObjectMethodName"); if (string.IsNullOrWhiteSpace(delegateSource)) throw new ArgumentNullException("DelegateSource"); if (string.IsNullOrWhiteSpace(delegateFullName)) throw new ArgumentNullException("DelegateFullName"); #endregion #region Copy values for properties this.ObjectSource = objectSource; this.ObjectFullName = objectFullName; this.ObjectMethodName = objectMethodName; this.DelegateSource = delegateSource; this.DelegateFullName = delegateFullName; this.SuggestedBinding = suggestedBinding; #endregion } public Delegate ToDelegate() { return ToDelegate(null); } public Delegate ToDelegate(Object linkObject) { return RD.Runtime.RuntimeDelegate.RuntimeDelegateUtility.Create(this, linkObject); } } }