2013-07-19 319 views
5

围绕着我的大脑围绕这无济于事,想知道有没有人可以帮忙?ViewModelBuilder泛型铸造问题

得到一个非常令人沮丧的演员问题,我肯定会有一个快速的答案,但可能只是由于我对泛型类型推断的理解有限而引起的。

在此先感谢!

场景是一个向导站点的一些“步骤”ViewModels。我为每个类创建了Builder类,并使用工厂来获取发布给我的步骤的特定构建器,这是一个IStepViewModel集合。

public interface IStepViewModelBuilderFactory 
{ 
    IStepModelBuilder<T> Create<T>(T stepViewModel) where T : IStepViewModel; 
    void Release<T>(IStepModelBuilder<T> stepViewModelBuilder) where T : IStepViewModel; 
} 

public interface IStepViewModel 
{ 
} 

public interface IStepModelBuilder<TStepViewModel> : IModelBuilder<TStepViewModel> where TStepViewModel : IStepViewModel 
{ 
} 

public class SpecificViewModelBuilder : IStepModelBuilder<SpecificStepViewModel> 
{ 
} 

public class SpecificStepViewModel: StepViewModel 
{ 
} 

public abstract class StepViewModel : IStepViewModel 
{ 
} 

失败的测试!

[Test] 
public void TestResolution() 
{ 
    var factory = this.container.Resolve<IStepViewModelBuilderFactory>(); 

    IStepViewModel viewModel = new SpecificStepViewModel(); 

    var builder = factory.Create(viewModel); // Here 

    Assert.That(builder, Is.Not.Null); 
} 

问题!

无法转换类型 'Company.Namespace.SpecificViewModelBuilder' 的目的是键入 'Company.Namespace.Builders.IStepModelBuilder`1 [Company.Namespace.IStepViewModel]'。

container.AddFacility<TypedFactoryFacility>(); 

    .... 

    container 
     .Register(
      Classes 
       .FromAssemblyContaining<StepViewModelSelector>() 
       .BasedOn<StepViewModelSelector>()); 

    container 
     .Register(
      Component 
       .For<IStepViewModelBuilderFactory>() 
       .AsFactory(c => c.SelectedWith<StepViewModelSelector>())); 

堆栈跟踪:使用Castle.Windsor

public class StepViewModelSelector : DefaultTypedFactoryComponentSelector 
{   
    protected override Type GetComponentType(System.Reflection.MethodInfo method, object[] arguments) 
    { 
     var arg = arguments[0].GetType(); 
     var specType = typeof(IModelBuilder<>).MakeGenericType(arg); 
     return specType; 
    } 
} 

登记这个

厂默认地将Impl如下

System.InvalidCastException是由用户代码未处理
的HResult = -21 47467262消息=无法投射 'Company.Namespace.SpecificViewModelBuilder'类型的对象以键入 'Company.Namespace.IStepModelBuilder`1 [Company.Namespace.IStepViewModel]'。 源= DynamicProxyGenAssembly2堆栈跟踪: 在Castle.Proxies.IStepViewModelBuilderFactoryProxy.Create [T](T stepViewModel) 在Tests.Infrastructure.ViewModelBuilderFactoryTests.TestResolution()在 C:\项目\基础设施\ ViewModelBuilderFactoryTests.cs:线61
的InnerException:

编辑:IModelBuilder<T>接口

public interface IModelBuilder<TViewModel> 
{ 
    TViewModel Build(); 
    TViewModel Rebuild(TViewModel model); 
} 
+0

看起来像在该工厂的Create方法的实现内发生错误。你能提供吗? – br1

+0

我只是使用Castle.Windsor的Typed工厂impl - 在过滤器选择器im中使用添加。 – M05Pr1mty

+0

好的,如果可以的话,请添加例外的.ToString()转储。我希望看到堆栈跟踪 – br1

回答

4

有一个接口,你在这里没有显示,这是IModelBuilder<T>界面,但它是解决您的问题的关键接口。

我猜想它目前像这样定义

public interface IModelBuilder<T> { } 

如果你使用通用的协方差,这是因为可用。NET 4,你就可以通过定义这样的接口来解决问题:

public interface IModelBuilder<out T> { } 

out修改使你的接口协变,这将让你投从IStepModelBuilder<SpecificStepViewModel>IStepModelBuilder<IStepViewModel>。您应该注意,这也会对您的界面设置一个限制,这将不允许它将任何方法定义为T作为参数,但仅作为返回值。

您可以阅读更多关于Covariance and Contravariance here

编辑

正如您在您的评论中提到,你的界面可能看起来是这样的:

public interface IModelBuilder<T> 
{ 
    T Create(T myViewModel); 
} 

如果强似T作为参数来Create,它的确定,为您传递IStepViewModelT而非其他任何东西,则应该解决您的问题:

public interface IModelBuilder<out T> 
{ 
    T Create(IStepViewModel myViewModel); 
} 

如果没有,那么你的尝试演员真的不应该被允许。

+0

我也看到了这一点,我将添加接口,但我不认为我能够使用协变与此即时消息使用类型作为输入参数也因此,据我所知,它需要是不变的? – M05Pr1mty

+0

谢谢你 - 真的很有用。正因如此,是否有任何方法重构这整个事情来实现我想要实现的目标? – M05Pr1mty

+0

@ M05Pr1mty查看我的更新回答 –

2

我认为以下两种定义都没有compatibile

public interface IStepViewModelBuilderFactory 
{ 
    IStepModelBuilder<T> Create<T>(T stepViewModel) where T : IStepViewModel; 
    //... rest of the class definition 
} 

public class SpecificViewModelBuilder : IStepModelBuilder<SpecificStepViewModel> 
{ 
} 

当创建运行时,它施放生产型(即SpecificViewModelBuilder)对他的返回值,这是IStepModelBuilder<T>

这不能做,你可以尝试进行测试,以手动执行此操作:

class MyTest<T> where T : IStepViewModel 
    { 
     void Test() 
     { 
      IStepModelBuilder<T> cannotImplicitlyCast = new SpecificStepViewModelBuilder(); 
     } 
    } 

编辑:一些(可能不太好)的想法

这是可以做到:

public class ViewModelBuilder<T> : IStepModelBuilder<T> where T : IStepViewModel 
{ 
} 

class MyTest<T> where T : IStepViewModel 
{ 
    void Test() 
    { 
     IStepModelBuilder<T> ok= new ViewModelBuilder<T>(); 
     IStepModelBuilder<SpecificStepViewModel> alsoOk = new ViewModelBuilder<SpecificStepViewModel>(); 
    } 
} 

so so you can speciali ze工厂,每个工厂一个SpecificStepViewModel

+0

我同意 - 您对如何解决此问题有任何建议吗?要么我一直在看这个太久或超越我了! – M05Pr1mty

+0

有趣的想法 - 我可能会在以后回来。也许某种抽象的事实可能会被包裹在这里来提供正确的特定工厂......谢谢不管! – M05Pr1mty