我正在尝试编写一个通用函数,该函数应该能够解析xml文件
这是代码
public struct XmlArg
{
public string Name;
public Type T;
public object Value;
};
static bool ParseXmlArgs(XmlReader xml, params XmlArg[] args)
{
for (int i = 0; i < args.Length; ++i)
{
if (xml.MoveToContent() != XmlNodeType.Element || xml.Name != args[i].Name)
{
return false;
}
args[i].Value = xml.ReadElementContentAs(args[i].T, null);
}
return true;
}
static void Main(string[] args)
{
int a = 0;
ParseXmlArgs(
XmlTextReader.Create("C:\Users\Yazilim\Desktop\XML.xml"),
new XmlArg[]{
new XmlArg() { Name = "ErrorCode", T = typeof(int), Value = a}});
}
我知道我应该把指针传给Value(它的类型当然应该是对象以外的其他类型)
但我不希望它是非管理的方式。
在结构中有没有使用变量指针的托管方法?
(函数可能错误或不正确,这不是重点)
简短回答:创建一个数组,将其传递给一个方法,然后读取其内容:
在调用该方法之前,您需要构造XmlArg[]
数组,并在解析完成之前忘记a
变量:
var reader = XmlTextReader.Create("C:\Users\Yazilim\Desktop\XML.xml");
var results = new []
{
new XmlArg() { Name = "ErrorCode", T = typeof(int) },
new XmlArg() { Name = "ErrorMessage", T = typeof(string) }
};
if (ParseXmlArgs(reader, results) == true)
{
// results were passed by reference, so do something with these values
}
长答案:
引用类型的变量(它是.NET中的任何class
)包含对其数据的引用,其中数据分配在内存中的不同位置,以及有关该类型的其他信息。与此相反,值类型的变量(如int
、float
或任何struct
)包含实际值-所有数据都包含在该变量中,不存在其他元数据。
当您将值类型变量作为逐值参数(这是.NET中传递参数的默认方式)传递给方法时,会生成该值的浅层副本。这意味着所有的数据,无论struct
有多大(在这种情况下,它只是int
),都会被复制,并且方法内部的值发生的任何事情都不会对初始值产生任何影响。另一方面,如果按值将引用类型变量传递给方法,该方法将获得引用的副本(本质上是指针),但引用指向的原始数据不会被复制,这意味着方法内部复制的引用仍然指向唯一的对象实例,并且该方法对数据所做的任何事情都反映在该方法之外。
使用ref
参数,可以使方法通过引用接受其参数。通过引用传递值类型将创建对原始值的引用,而不复制数据,类似于通过值传递引用类型时发生的情况。在这种情况下,修改方法内部的引用值会修改方法外部变量中的原始值,因为没有创建原始数据的副本。如果您使用ref
关键字通过引用逐个传递引用类型,那么您实际上是在传递对实际数据引用的引用,这意味着该方法现在甚至有机会使原始变量指向完全不同的对象(除了修改原始对象之外)。
此外,将值类型强制转换为object
(当您将其分配给XmlArg.Value
属性时会发生这种情况),将值框起来。再次对值类型进行装箱将在内存中的其他位置创建值的副本,并创建指向该副本的引用类型(object
)。同样,到原始值的"链接"丢失了,这意味着从那时起对该对象所做的任何操作都不可能对原始变量产生任何影响。
所以,在你的情况下,你基本上有:
// a value type variable
int a = 0;
// implicit boxing occurs, creating a copy of the value
// ("link" to a is lost)
object boxed_a = (object)a;
// a value type (struct) variable which contains a member which points
// to the boxed int value 0
XmlArg arg = new XmlArg() { Value = boxed_a };
// here, you are creating an array of XmlArg, but since XmlArg is a struct,
// the array actually contains a *copy* of the `arg` value
// ("link" to arg is also lost)
XmlArg[] args = new [] { arg };
在这种情况下,有一件事可能会有所帮助,那就是lambdas中的变量闭包。从本质上讲,在匿名方法中使用变量会创建对原始值的实际引用,这允许您从任何地方对其进行修改,但真正的原因是编译器实际上将原始值作为字段放置在为lambda创建的匿名类中:从本质上说,它不再是一个简单的旧局部变量,这允许lambda处理原始数据。
在你的情况下,你会做一些类似的事情:
// define a common interface (Value is an object)
interface IXmlArg
{
string Name { get; }
Type Type { get; }
object Value { get; set; }
}
// I used generics to get type safety and to get type T information "for free"
class XmlArg<T> : IXmlArg
{
public XmlArg(string name, Action<T> setter, Func<T> getter)
{
_name = name;
_setter = setter;
_getter = getter;
}
private readonly string _name;
public string Name { get { return _name; } }
public Type Type { get { return typeof(T); } }
private readonly Func<T> _getter;
private readonly Action<T> _setter;
public object Value
{
get { return (object)_getter(); }
set { _setter((T)value); }
}
}
在你的主要方法中,你会做:
// local value type variable
int a = 0;
// we are creating two lambdas, one to set the value to a, and one to read it
IXmlArg arg = new XmlArg<int>("ErrorCode", x => a = x, () => a);
// this method will use the `Value` setter, which in turns calls the lambda
ParseXmlArgs(reader, new [] { arg });
请注意,结构体XmlArg
是一种可变值类型,许多人认为它是"邪恶的"。
也许您希望Value
成员是对具有可以更改的整数的类型的引用?如果你写:
public class ChangeableInt32 // 'class' gives a reference type
{
public int Content;
}
public struct XmlArg
{
public string Name;
public Type T;
public ChangeableInt32 Value;
}
然后:
args[i].Value.Content = /* read the int */;