我有一個擁有委託成員的類。 我可以爲該類的每個實例化對象設置委託,但尚未找到任何方法來保存該對象。我們可以將代表保存在一個文件中(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. c#將我的數據表保存到一個[txt]文件
- 4. 我們可以使用一個python變量來保存整個文件嗎?
- 5. 將一個可變文件保存到一個文件夾中
- 6. AVAudioRecorder:我們可以記錄而不保存到文件中嗎?
- 7. 我們可以將fadeOut方法存儲在一個變量中
- 8. 我們可以運行保存在jar文件中的ant文件嗎?
- 9. 我們可以在C文件中定義一個C++方法嗎?
- 10. C#將一個數組列表保存到一個文件?
- 11. 在Objective-C中可以將整個類保存在.m文件中嗎?
- 12. 我們可以保存一個圖像到.doc文件與AS 3.0
- 13. 我們可以在wso2esb的文本文件中存儲一個字符串嗎?
- 14. 我可以將SQLite文件保存到磁盤,然後在另一個SQLite會話中使用它們嗎?
- 15. 我們可以在文件存儲中創建臨時表,不在內存中
- 16. 我們可以將通知保存到iphone/ipad的SMS文件夾中嗎?
- 17. 我們可以將兩個文件與main()放在makefile中嗎?
- 18. 我們可以用c寫一個word文件嗎?
- 19. 我們可以將ckeditor數據保存到我的網站的doc文件
- 20. 我在哪裏可以在Ubuntu上保存我的C代碼
- 21. c#MS Excel Application.ActiveSheet,我可以將它保存在單獨的文件中嗎?
- 22. 我可以序列化一個數組列表,以便我可以將其存儲在.dat文件中嗎?
- 23. 我們可以將C++文件添加到iOS項目中嗎?
- 24. 將文件保存在C
- 25. 我們可以用一個變量將一個類保存在一個變量中嗎?
- 26. 我們可以在apk文件中捆綁一個目錄嗎?
- 27. 我們可以在android中打開一個swf文件嗎?
- 28. 閱讀一個文件夾中的所有xml文件,並將它們保存在一個數據表中
- 29. 我在哪裏可以更改此Python代碼片段以將臨時文件保存在tmp文件夾中?
- 30. 是否可以將任意數據保存到C#文件中?
你確定這個代理引用了一個非靜態方法嗎?我可以看到它使用靜態方法,因爲不需要定義Traget,但是例如方法它有什麼作用?潛在地,它可以序列化Target實例圖(假設它是可序列化的),但是隨後在反序列化和調用時它將位於具有潛在陳舊數據的不同實例上。我個人會非常小心地選擇以這種方式堅持代表,因爲它很容易導致一些意想不到且難以調試/修復的行爲。 – LBushkin 2009-07-15 17:53:44
它也適用於非靜態方法。它序列化Target實例圖,並假設它是可序列化的(用SerializableAttribute標記)。 – 2009-07-15 18:05:26