Rac*_*hel 14 c# reflection .net-3.5
我有以下课程
public abstract class BaseViewPresenter { }
public abstract class BaseView<T> : UserControl
where T : BaseViewPresenter { }
public class LoginPresenter : BaseViewPresenter { }
public partial class LoginView : BaseView<LoginPresenter> { }
Run Code Online (Sandbox Code Playgroud)
我有一个看起来像这样的方法(简化)
public BaseView<BaseViewPresenter> Resolve(BaseViewPresenter model)
{
var type = model.GetType();
var viewType = _dataTemplates[type];
// Correctly creates BaseView object
var control = Activator.CreateInstance(viewType);
// Fails to cast as BaseView<BaseViewPresenter> so returns null
return control as BaseView<BaseViewPresenter>;
}
Run Code Online (Sandbox Code Playgroud)
当我使用LoginPresenter的实例调用它时
var login = new LoginPresenter();
var ctl = Resolve(login);
Run Code Online (Sandbox Code Playgroud)
该行Activator.CreateInstance(viewType)正确解析为我的新实例LoginView,但control as BaseView<BaseViewPresenter>无法正确执行转换,因此返回null.
有没有办法在不使用特定类型泛型的情况下正确地control转换成BaseView<BaseViewPresenter>?
由于LoginView继承自BaseView<LoginPresenter>并LoginPresenter继承BaseViewPresenter,我认为有一种方法可以转换LoginView为BaseView<BaseViewPresenter>.
我坚持使用.Net 3.5
Eri*_*ert 48
这是一个非常常见的问题.让我们重命名你的类型:
abstract class Fruit { } // was BaseViewPresenter
abstract class FruitBowl<T> where T : Fruit // was BaseView
class Apple : Fruit { } // was LoginPresenter
class BowlOfApples : FruitBowl<Apple> { } // was LoginView
Run Code Online (Sandbox Code Playgroud)
你现在的问题是:
我有一个
BowlOfApples,继承自FruitBowl<Apple>.为什么我不能用它FruitBowl<Fruit>?苹果是一种水果,所以一碗苹果就是一碗水果.
不,不是.你可以把香蕉放在一碗水果里,但你不能把香蕉放在一碗苹果里,因此一碗苹果不是一碗水果.(通过类似的论证,一碗水果也不是一碗苹果.)由于你可以在两种类型上合法地执行的操作是不同的,它们是不兼容的.
这是StackOverflow传奇人物Jon Skeet的照片,展示了这一事实:

您想要的功能称为通用逆变,当编译器可以证明方差是安全的,并且变化类型是引用类型时,它仅在接口和委托类型上受支持.例如,您可以IEnumerable<Apple>在IEnumerable<Fruit>需要的上下文中使用a,因为编译器可以验证您无法将a Banana放入一系列水果中.
在本网站或网站上搜索"C#协方差和逆变",您将找到有关此功能如何工作的更多详细信息.特别是,关于我们如何在C#4中设计和实现此功能的系列文章从这里开始:http://blogs.msdn.com/b/ericlippert/archive/2007/10/16/covariance-and-convvariance-in -C-部分one.aspx
Rac*_*hel 10
我接受了Eric的答案,因为它提供了一个很好的解释,为什么我想要的是不可能的,但我也认为我会分享我的解决方案以防其他人遇到同样的问题.
我从原始BaseView类中删除了泛型类型参数,并创建了该类的第二个版本,BaseView其中包含泛型类型参数及其详细信息.
第一个版本由我的.Resolve()方法或其他不关心特定类型的代码使用,第二个版本由任何关心的代码使用,例如实现BaseView
这是我的代码最终看起来的一个例子
// base classes
public abstract class BaseViewPresenter { }
public abstract class BaseView : UserControl
{
public BaseViewPresenter Presenter { get; set; }
}
public abstract class BaseView<T> : BaseView
where T : BaseViewPresenter
{
public new T Presenter
{
get { return base.Presenter as T; }
set { base.Presenter = value; }
}
}
// specific classes
public class LoginPresenter : BaseViewPresenter { }
public partial class LoginView : BaseView<LoginPresenter>
{
// Can now call things like Presenter.LoginPresenterMethod()
}
// updated .Resolve method used for obtaining UI object
public BaseView Resolve(BaseViewPresenter presenter)
{
var type = model.GetType();
var viewType = _dataTemplates[type];
BaseView view = Activator.CreateInstance(viewType) as BaseView;
view.Presenter = presenter;
return view;
}
Run Code Online (Sandbox Code Playgroud)