2

[编辑:我在这个问题中添加了很多细节,以便更清楚为什么我需要通过引用传递枚举器]

我正在编写一些代码来解析由命令和参数组成的列表。并非所有命令都具有相同数量的参数。例如,列表可能是 -

command1,
100,
command2,
54,
42,
71,
command3,
10,
31,
command1,
82,
command3,
102,
87

(请注意,某些命令可能具有非整数参数)

我正在使用 List 枚举器遍历数据列表。每个命令都有自己的类,能够从列表中解析命令的参数(并执行与该命令相关的一系列其他活动,这些活动在此缩减示例中不需要)。

我有一本字典,将命令连接到它们的类 -

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 }));
}

(所有命令类都派生自相同的基本类型 Block)

因此,在每个命令类的构造函数中,它可以解析该命令需要多少个参数,并且在返回主循环时,枚举器将继续遍历这些参数。

例如 -

class Command1Class : Block
{
    string param;

    public Command1Class(ref List<object>.Enumerator enumerator)
    {
        enumerator.MoveNext();
        param = (string)enumerator.Current;
    }
}

但是我发现枚举器仅在构造函数中本地修改。因此,在从构造函数返回时,枚举器仍然指向命令,而不是继续遍历该命令需要多少参数。

如果我使用以下样式以非动态方式执行此操作,它会按预期工作,枚举器从构造函数返回时指向下一个命令 -

new SomeClass(ref enumerator)

所以我想知道为什么我的 CreateInstance 代码没有按预期工作,我怎么能动态地做到这一点?

4

2 回答 2

1

你的方法有很多问题,因为Enumerator是 struct (所以,值类型)。这意味着(除其他外)它在许多情况下都会被复制,例如,如果您删除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类中创建方法:

public void Init(ref List<object>.Enumerator enumerator)

然后不加思索地调用它。

于 2017-05-06T17:30:42.730 回答
0

不要使用 ref 你需要传递 list 而不是 ref enumerator 并在构造函数中调用 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 });
    }
}
于 2017-05-06T13:54:44.350 回答