2017-05-06 609 views
1

[编辑:我加了很多细节,对这个问题,以使其更清晰,为什么我需要通过引用传递枚举]通过引用的CreateInstance传递参数

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

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

(请注意,某些命令可能有是非整数参数),我通过使用列表枚举数据的列表迭代

。每个命令都有它自己的类,它能够从列表中解析命令的参数(并执行一系列与此命令相关的其他活动,而这些活动并不需要用于此示例)。

我有一个命令连接到他们班一本字典 -

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

(所有命令类都从相同的基本类型派生的,块)

因此,在每个命令类的构造函数中,它可以解析命令采用的参数的数量,并且在返回到主循环时,枚举器将具有大量e通过这些参数继续前进。

例如 -

class Command1Class : Block 
{ 
    string param; 

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

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

如果我这样做非动态使用下面的风格,它的工作原理与枚举指向下一个命令时,它从构造函数返回预期 -

new SomeClass(ref enumerator) 

所以我不知道为什么我的代码与CreateInstance不能按预期工作,我如何动态地做到这一点?

+0

你预期会发生什么? – Evk

+0

“预期”是什么? – Crowcoder

+0

你是对的。我已经添加了更多信息来说明这样做的原因以及预期结果更加清晰 – Rok

回答

1

你的方法有很多问题,因为Enumerator是struct(so,value type)。这意味着(除其他事项外),它被在许多情况下,如果你删除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) 

然后调用它没有反映。

+0

非常好的解释 - 非常感谢。使用单独的Init函数似乎是一个很好的解决方案,所以我会继续。再次感谢 – Rok

0

不要使用引用你需要通过列表而不是引用枚举器并在构造函数内调用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 }); 
    } 
} 
+0

感谢您的回复。我为原始问题添加了更多的细节,以便更清楚地说明为什么我需要通过引用以及我期待的结果来通过枚举器 – Rok