我有一个值(结构实例)被转换object为通用处理.我需要复制一下这个值.我不能明确地这样做,因为我只是拥有它,Type并且不知道它在编译时是什么.
默认情况下,我得到一份参考文献:var copy = objectOfMyStruct;.我想过制作一个明确的浅拷贝,MemberwiseClone()但我不能这样做,因为它是受保护的方法,我无法修改MyStruct.
Convert.ChangeType(objectOfMyStruct, typeOfMyStruct) 没有帮助,因为转换(实际上没有转换)发生在里面,它再次返回对象.
我怎么能这样做?
编辑:
我需要制作一个副本以保留原始值,并将其反序列化为传递给OnChangeHandler.简化的实施是:
var oldValue = type.GetValue(reference);
var newValue = oldValue; // something better is needed here
Deserialize(type, stream, ref newValue);
OnChange(oldValue, newValue);
type.SetValue(reference, newValue);
Run Code Online (Sandbox Code Playgroud)
复制是因为只发送了delta(更改),因此应该应用于原始值.
编辑2:
这个实现适用于原始类型,所以尽管int也是盒装的,我正在复制它而不是复制对它的引用.我只是不明白这一点.
这是一个需要的例子.
您可以在LINQPad中测试的此示例应该复制该结构而不将其转换回其未装箱的类型,这样当通过实现的接口调用它进行变异时,只会突变原始结构.因此问题是; 我该如何编写克隆方法?
void Main()
{
object original = new Dummy { Property = 42, Field = "Meaning of life" };
object clone = Clone(original);
((IDummy)original).Mutate(); // will modify the boxed struct in-place
original.Dump();
// should output different if Clone did its job
clone.Dump();
}
static object Clone(object input)
{
return input;
}
public interface IDummy
{
void Mutate();
}
public struct Dummy : IDummy
{
public int Property { get; set; }
public string Field;
public void Mutate()
{
Property = 77;
Field = "Mutated";
}
}
Run Code Online (Sandbox Code Playgroud)
我假设您不仅要制作副本,还要能够实际使用该副本.
并且为了使用它,您需要将其转换(取消)到适当的类型,这样可以有效地复制它.实际上,即使将价值放入盒子中也已经产生了副本.
所以,如果(例如)你知道这些对象是int或者浮点数,你可以这样做:
if (obj is int)
{
int i = (int) obj;
// do something with the copy in i
}
else if (obj is float)
{
float f = (float) obj;
// do something with the copy in f
}
Run Code Online (Sandbox Code Playgroud)
如果要评估大量类型,可以使用switch语句甚至是Dictionary<Type,Action<object>>.
如果你需要在编译时处理你不知道的类型(某些类型动态地添加某种类型的插件机制),那么这是不可能的,但是再一次,它也不可能做任何事情与对象(除非通过接口).
更新:
既然你改变了问题,这里有一个更好的答案:你不需要复制,它是通过装箱结构为你做的.
例:
int i = 42;
// make a copy on the heap
object obj = i;
// modify the original
i = i + 1;
// copy is not modified
Debug.Assert((int)obj == 42);
Run Code Online (Sandbox Code Playgroud)
显然,我在int这里使用是为了方便,但对于每个结构都是如此.如果struct实现了一个接口,你可以将该对象强制转换为该接口(不会生成第二个副本)并使用它.它不会修改原始值,因为它在框中的副本上运行.
更新2:
只是非常明确:这适用于每个结构.例如:
interface IIncrementor
{
void Increment();
}
struct MyStruct : IIncrementor
{
public int i;
public void Increment()
{
this.i = this.i + 1;
}
public override string ToString()
{
return i.ToString();
}
}
// in some method:
MyStruct ms = new MyStruct();
ms.i = 42;
Console.Writeline(ms); // 42
object obj = ms;
IIncrementable ii = (IIncrementable) obj;
ii.Increment();
Console.Writeline(ms); // still 42
Console.Writeline(ii); // 43
Run Code Online (Sandbox Code Playgroud)
还有一个更新:
代替
object original = new Dummy { Property = 42, Field = "Meaning of life" };
object clone = Clone(original);
Run Code Online (Sandbox Code Playgroud)
写
Dummy original = new Dummy { Property = 42, Field = "Meaning of life" };
object clone = original;
Run Code Online (Sandbox Code Playgroud)
你会没事的
感谢 LINQPad 示例,它极大地澄清了您的问题,并为我提供了提出解决方案的起点。
这是一个非常暴力的解决方案,但它可能会达到您的目的,直到有人想出一个更优雅的答案:
static object Clone(object input)
{
IntPtr p = Marshal.AllocHGlobal(Marshal.SizeOf(input));
try
{
Marshal.StructureToPtr(input, p, false);
return Marshal.PtrToStructure(p, input.GetType());
}
finally
{
Marshal.FreeHGlobal(p);
}
}
Run Code Online (Sandbox Code Playgroud)
它是这样工作的:
StructureToPtr拆箱您的输入并将其复制到非托管内存中:
如果结构是值类型,则可以将其装箱或拆箱。如果已装箱,则在复制前将其拆箱。
PtrToStructure创建一个新结构,将其装箱并返回:
您可以将值类型传递给此重载方法。在这种情况下,返回的对象是装箱实例。
非托管内存被释放。
| 归档时间: |
|
| 查看次数: |
1817 次 |
| 最近记录: |