我有一个拥有委托成员的类。 我可以为该类的每个实例化对象设置委托,但尚未找到任何方法来保存该对象。我们可以将代表保存在一个文件中(C#)
回答
这是一个相当危险的事情。
尽管确实可以像其他任何对象一样序列化和反序列化委托,但委托是指向序列化它的程序内的方法的指针。如果你在另一个程序中反序列化对象,那么你会得到一个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;
}
}
运行它,你会看到它创建的对象,将其序列,反序列化到一个新的对象,当你调用Del
在新对象上返回“a”。优秀。好的,现在注释掉WriteFoo
的调用,以便程序只是反序列化对象。再次运行该程序,您会得到相同的结果。
现在交换a和b的声明并运行该程序。让人惊讶。现在反序列化的对象返回“b”。
发生这种情况是因为实际上被序列化的是编译器分配给lambda表达式的名称。编译器按照发现它们的顺序将名称分配给lambda表达式。
这就是这样的风险:你没有序列化委托,你在序列化一个符号。这是符号的值,而不是符号代表的序列化。反序列化对象的行为取决于该符号的值在反序列化它的程序中表示的内容。
在某种程度上,所有序列化都是如此。将对象反序列化为一个与序列化程序不同的实现对象类的程序,乐趣就开始了。但序列化委托将序列化的对象耦合到序列化它的程序的符号表,而不是对象的类的实现。
如果是我,我会考虑明确这个耦合。我会创建一个Foo
的静态属性,它是Dictionary<string, Func<string>>
,用键和函数填充它,并在每个实例中存储密钥而不是函数。这使得反序列化程序负责填充字典,然后开始反序列化Foo
对象。在某种程度上,这与使用BinaryFormatter
来序列化委托所做的完全相同;不同之处在于这种方法使得反序列化程序将功能分配给符号的责任更加明显。
实际上,您可以使用BinaryFormatter,因为它保留了类型信息。而这里的证明:
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的是,它的格式是没有很好的记载和实施可能有.NET和/或CLR版本之间的重大更改的。
委托是一种方法指针,当您说保存时,我可能会误解,但如果尝试保存并恢复地址,则在运行时添加到委托的位置可能不再存在。
谢谢Quintin。 你是对的,作为一个指针,我们不能。但是他们的内容呢?类似于C++ *操作符。 – 2009-07-15 17:22:48
所以,这是我的理解,你想“保存”一个函数指针(委托)。现在,如果将所有委托函数放入库中,则可以使用系统反射在运行时构建链接,然后可以选择将委托转换为编译器定义的委托(该库也将位于库中)。唯一的缺点是目标方法必须是一个明确定义的位置,所以没有匿名方法,因为每次编译时都会在编译时定义位置。 下面是我能够在运行时重新创建委托的代码,需要您自担风险并且没有备注。
更新:您可以做的另一件事是创建一个自定义属性,并将其应用于您想要创建到委托中的任何和所有方法。在运行时,使用系统反射,遍历找到的导出类型,然后从具有自定义属性的类型中选择所有方法。这可能比你想要的要多,如果你还提供了一个“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);
}
}
}
- 1. 我如何将它保存到一个变量中,以便我可以将它保存到文件中
- 2. 我们可以将textarea中的数据保存到文本文件中吗?
- 3. 我们可以使用一个python变量来保存整个文件吗?
- 4. 将一个可变文件保存到一个文件夹中
- 5. AVAudioRecorder:我们可以记录而不保存到文件中吗?
- 6. 我们可以运行保存在jar文件中的ant文件吗?
- 7. C#将一个数组列表保存到一个文件?
- 8. 在Objective-C中可以将整个类保存在.m文件中吗?
- 9. 我们可以在wso2esb的文本文件中存储一个字符串吗?
- 10. 我们可以在文件存储中创建临时表,不在内存中
- 11. 我们可以将通知保存到iphone/ipad的SMS文件夹中吗?
- 12. 我在哪里可以在Ubuntu上保存我的C代码
- 13. 我可以序列化一个数组列表,以便我可以将其存储在.dat文件中吗?
- 14. 将文件保存在C
- 15. 我们可以用一个变量将一个类保存在一个变量中吗?
- 16. 阅读一个文件夹中的所有xml文件,并将它们保存在一个数据表中
- 17. 我在哪里可以更改此Python代码片段以将临时文件保存在tmp文件夹中?
- 18. 是否可以将任意数据保存到C#文件中?
- 19. 我们可以通过hiredis保存Redis中的C结构吗?我可以保存但不能得到它
- 20. 我们可以将字符串保存为OpenTSDB中的值吗?
- 21. 我可以保存一个对象到app.config文件吗?
- 22. 做一个表列可编辑并保存在一个文件
- 23. 我们可以将对象存储在文件中供以后检索吗?
- 24. 将excel文件保存到C#代码中的csv文件中
- 25. 我可以将连接表的输出保存到另一个表中吗?
- 26. 我可以将HTML文件存储在drawable folde中,并将它们用于webview
- 27. 我可以将文件块保存在不同的文件夹中
- 28. Java Jsp - 我们可以将输出保存为PDF或Word文件吗?
- 29. 我可以在同一个循环中保存Mongo文档吗?我在PyMongo中发现它们?
- 30. 我们可以在C#.net
你确定这个代理引用了一个非静态方法吗?我可以看到它使用静态方法,因为不需要定义Traget,但是例如方法它有什么作用?潜在地,它可以序列化Target实例图(假设它是可序列化的),但是随后在反序列化和调用时它将位于具有潜在陈旧数据的不同实例上。我个人会非常小心地选择以这种方式坚持代表,因为它很容易导致一些意想不到且难以调试/修复的行为。 – LBushkin 2009-07-15 17:53:44
它也适用于非静态方法。它序列化Target实例图,并假设它是可序列化的(用SerializableAttribute标记)。 – 2009-07-15 18:05:26