在 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)

然后不经反射调用