在 CreateInstance 中通过引用传递参数
Passing parameters by reference in CreateInstance
[编辑:我在这个问题中添加了很多细节,以便更清楚地说明为什么我需要通过引用传递枚举器]
我正在编写一些代码来解析由命令和参数构成的列表。并非所有命令都具有相同数量的参数。例如,列表可以是 -
command1,
100,
command2,
54,
42,
71,
command3,
10,
31,
command1,
82,
command3,
102,
87
(请注意,某些命令可能具有非整数参数)
我正在使用 List 枚举器遍历数据列表。每个命令都有自己的 class,它能够从列表中解析命令的参数(并执行一系列与命令相关的其他活动,这些活动对于这个缩减示例来说是不需要的)。
我有一本字典将命令连接到它们的 classes -
var map = new Dictionary<string, Type>
{
{ "command1", typeof(Command1Class) },
{ "command2", typeof(Command2Class) },
{ "command3", typeof(Command3Class) },
};
所以我的基本解析循环如下-
var enumerator = data.GetEnumerator();
while (enumerator.MoveNext())
{
var command = (string)enumerator.Current;
codeBlock.AddBlock((Block)Activator.CreateInstance(map[command], new object[] { enumerator }));
}
(所有命令 classes 派生自相同的基本类型,Block)
因此在每个命令 class 的构造函数中,它可以解析该命令采用的参数数量,并且在 return 进入主循环后,枚举器将继续处理这些参数.
例如-
class Command1Class : Block
{
string param;
public Command1Class(ref List<object>.Enumerator enumerator)
{
enumerator.MoveNext();
param = (string)enumerator.Current;
}
}
但我发现枚举数仅在构造函数中进行了局部修改。因此,从构造函数 return 开始,枚举器仍指向命令,而不是继续查看该命令需要多少参数。
如果我使用以下样式非动态地执行此操作,它将按预期工作,枚举器指向构造函数中的下一个命令时 returns -
new SomeClass(ref enumerator)
所以我想知道为什么我的 CreateInstance 代码没有按预期工作,我如何动态地执行此操作?
不要使用 ref 你需要传递列表而不是 ref 枚举器并在构造函数中调用 getEnumerator。
class SomeClass
{
public SomeClass(List<object> list)
{
var enumerator = list.GetEnumerator();
enumerator.MoveNext();
}
}
class Program
{
static void Main(string[] args)
{
var list = new List<object>();
var someClass = Activator.CreateInstance(typeof(SomeClass),
new object[] { list = list });
}
}
你的方法有很多问题,因为 Enumerator
是结构(所以,值类型)。这意味着(除其他外)它在许多情况下都会被复制,例如,如果您删除 ref
关键字:
public Command1Class(List<object>.Enumerator enumerator)
然后传递您的枚举器 - 它将被复制并且 Command1Class
构造函数内部的枚举器与外部的实例不同。您注意到了这一点并在构造函数中通过引用传递了枚举器,但是当您这样做时同样的问题再次困扰您:
(Block)Activator.CreateInstance(map[command], new object[] { enumerator })
您在 object[]
数组中传递了枚举器(正如 CreateInstance
所期望的那样),这再次生成了一个副本。这样更清楚:
var args = new object[] { enumerator };
Activator.CreateInstance(typeof(Command1Class), args);
当您创建 args
数组时 - 它已经包含 enumerator
的副本,而不是同一个实例。现在这个副本通过引用传递给你的构造函数并且确实是高级的,你可以这样验证:
var args = new [] { enumerator };
Activator.CreateInstance(typeof(Command1Class), args);
// assign copy back to original value
enumerator = (List<object>.Enumerator) args[0];
// now enumerator is advanced as expected
实际上如果 enumerator
是引用类型也会发生同样的情况,因为当你把它放在数组中以将参数传递给 Activator.CreateInstance
- 它不再是同一个变量(它指向同一个对象在内存中,但指针本身不同)。
所以您的方法非常脆弱,您可能应该重新设计它。如果你想坚持下去 - 不要将任何东西传递给构造函数,而是在 Block
class:
处创建方法
public void Init(ref List<object>.Enumerator enumerator)
然后不经反射调用
[编辑:我在这个问题中添加了很多细节,以便更清楚地说明为什么我需要通过引用传递枚举器]
我正在编写一些代码来解析由命令和参数构成的列表。并非所有命令都具有相同数量的参数。例如,列表可以是 -
command1,
100,
command2,
54,
42,
71,
command3,
10,
31,
command1,
82,
command3,
102,
87
(请注意,某些命令可能具有非整数参数)
我正在使用 List 枚举器遍历数据列表。每个命令都有自己的 class,它能够从列表中解析命令的参数(并执行一系列与命令相关的其他活动,这些活动对于这个缩减示例来说是不需要的)。
我有一本字典将命令连接到它们的 classes -
var map = new Dictionary<string, Type>
{
{ "command1", typeof(Command1Class) },
{ "command2", typeof(Command2Class) },
{ "command3", typeof(Command3Class) },
};
所以我的基本解析循环如下-
var enumerator = data.GetEnumerator();
while (enumerator.MoveNext())
{
var command = (string)enumerator.Current;
codeBlock.AddBlock((Block)Activator.CreateInstance(map[command], new object[] { enumerator }));
}
(所有命令 classes 派生自相同的基本类型,Block)
因此在每个命令 class 的构造函数中,它可以解析该命令采用的参数数量,并且在 return 进入主循环后,枚举器将继续处理这些参数.
例如-
class Command1Class : Block
{
string param;
public Command1Class(ref List<object>.Enumerator enumerator)
{
enumerator.MoveNext();
param = (string)enumerator.Current;
}
}
但我发现枚举数仅在构造函数中进行了局部修改。因此,从构造函数 return 开始,枚举器仍指向命令,而不是继续查看该命令需要多少参数。
如果我使用以下样式非动态地执行此操作,它将按预期工作,枚举器指向构造函数中的下一个命令时 returns -
new SomeClass(ref enumerator)
所以我想知道为什么我的 CreateInstance 代码没有按预期工作,我如何动态地执行此操作?
不要使用 ref 你需要传递列表而不是 ref 枚举器并在构造函数中调用 getEnumerator。
class SomeClass
{
public SomeClass(List<object> list)
{
var enumerator = list.GetEnumerator();
enumerator.MoveNext();
}
}
class Program
{
static void Main(string[] args)
{
var list = new List<object>();
var someClass = Activator.CreateInstance(typeof(SomeClass),
new object[] { list = list });
}
}
你的方法有很多问题,因为 Enumerator
是结构(所以,值类型)。这意味着(除其他外)它在许多情况下都会被复制,例如,如果您删除 ref
关键字:
public Command1Class(List<object>.Enumerator enumerator)
然后传递您的枚举器 - 它将被复制并且 Command1Class
构造函数内部的枚举器与外部的实例不同。您注意到了这一点并在构造函数中通过引用传递了枚举器,但是当您这样做时同样的问题再次困扰您:
(Block)Activator.CreateInstance(map[command], new object[] { enumerator })
您在 object[]
数组中传递了枚举器(正如 CreateInstance
所期望的那样),这再次生成了一个副本。这样更清楚:
var args = new object[] { enumerator };
Activator.CreateInstance(typeof(Command1Class), args);
当您创建 args
数组时 - 它已经包含 enumerator
的副本,而不是同一个实例。现在这个副本通过引用传递给你的构造函数并且确实是高级的,你可以这样验证:
var args = new [] { enumerator };
Activator.CreateInstance(typeof(Command1Class), args);
// assign copy back to original value
enumerator = (List<object>.Enumerator) args[0];
// now enumerator is advanced as expected
实际上如果 enumerator
是引用类型也会发生同样的情况,因为当你把它放在数组中以将参数传递给 Activator.CreateInstance
- 它不再是同一个变量(它指向同一个对象在内存中,但指针本身不同)。
所以您的方法非常脆弱,您可能应该重新设计它。如果你想坚持下去 - 不要将任何东西传递给构造函数,而是在 Block
class:
public void Init(ref List<object>.Enumerator enumerator)
然后不经反射调用