2011-08-01 118 views
9

我有两个商务合同类:行动代表,泛型,协方差和逆变

public BusinessContract 

public Person : BusinessContract 

在另一大类,我有以下代码:

private Action<BusinessContract> _foo; 

public void Foo<T>(Action<T> bar) where T : BusinessContract 
{ 
    _foo = bar; 
} 

上面甚至不会编译,这让我有些困惑。我将T约束为BusinessContract,为什么编译器不知道bar可以分配给_foo?

在试图解决这个问题,我们试图将其更改为以下:

public void Foo<T>(Action<T> bar) where T : BusinessContract 
{ 
    _foo = (Action<BusinessContract>)bar; 
} 

现在,编译器是幸福的,所以我在其他地方写下面的代码在我的应用程序:

Foo<Person>(p => p.Name = "Joe"); 

运行时应用程序爆发InvalidCastException。

我不明白。我应该不能将更具体的类型转换为不太特定的类型并分配它?

UPDATE

乔恩回答了这个问题,从而得到了该点头,只是收在这个循环中,这里是我们如何最终解决问题。

private Action<BusinessContract> _foo; 

public void Foo<T>(Action<T> bar) where T : BusinessContract 
{ 
    _foo = contract => bar((T)contract); 
} 

我们为什么要这样做?我们有一个假的DAL,我们用于单元测试。使用其中一种方法,我们需要给测试开发人员指定在测试过程中调用方法时应该执行的操作(它是从数据库更新缓存对象的刷新方法)。 Foo的目的是设置在刷新被调用时应该发生的事情。 IOW,在这个课程的其他地方,我们有以下内容。

public void Refresh(BusinessContract contract) 
{ 
    if(_foo != null) 
    { 
     _foo(contract); 
    } 
} 

例如,测试开发人员可以决定当调用Refresh时他们想要将名称设置为不同的值。

Foo<Person>(p => p.Name = "New Name"); 

回答

13

你有错误的方式协变和逆变。我们来考虑Action<object>Action<string>。卸下实际仿制药,你试图做这样的事情:

private Action<object> _foo; 

public void Foo(Action<string> bar) 
{ 
    // This won't compile... 
    _foo = bar; 
} 

现在假设我们接着写:

_foo(new Button()); 

这很好,因为Action<object>可以传递任何对象...但我们已经用代理初始化它,其中必须接受一个字符串参数。哎哟。

这不是类型安全的,所以不能编译。

另一种方式工作虽然:

private Action<string> _foo; 

public void Foo(Action<object> bar) 
{ 
    // This is fine... 
    _foo = bar; 
} 

现在,当我们调用_foo,我们一个字符串传递 - 但是这很好,因为我们有一个委托初始化它这可以采用任何object引用作为参数,所以我们碰巧给它一个字符串很好。

所以基本上Action<T>逆变 - 而Func<T>

Func<string> bar = ...; 
Func<object> foo = bar; // This is fine 
object x = foo(); // This is guaranteed to be okay 

目前尚不清楚你想要什么与动作,所以很遗憾我真的不能给任何关于如何解决这个问题的建议......

+0

@MDeSchaepmeester:不,我不是 - 在工作代码中(后半部分),我将一个'Action '值('bar')赋值给一个'Action '变量('_foo')然后为'Func '变量('foo')分配一个'Func '值('bar)'。 –

+0

你是对的,愚蠢的错误,我没有真正读过这段代码,并假定你用_foo作为参数调用Foo(我来这里是因为我在做* *) – MarioDS

0

因为既然你使用的是逆变,而不是协变的,不能被分配,没有办法保证泛型类型可以被分配到foo中。

+0

那么,这不是真的“因为[OP]使用泛型” - 是因为'Action '是逆变而不是cov ariant。 –

+0

@Jon Skeet,你是​​对的,我没有解释得很好,我会编辑它。 –