在CreateInstance中通过引用传递参数

时间:2017-05-06 13:42:30

标签: c# ref createinstance

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

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

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代码没有按预期工作,我怎么能动态地执行此操作?

2 个答案:

答案 0 :(得分: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)

然后在没有反思的情况下调用它。

答案 1 :(得分:0)

不要使用ref你需要传递列表而不是ref枚举器并在constuctor中调用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 });
    }
}