ViewModelBuilder泛型投射问题

时间:2013-07-19 11:48:19

标签: c# .net generics casting

围绕这一点扯动我的大脑无济于事,想知道是否有人可以提供帮助?

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

提前致谢!

方案是向导网站的许多“步骤”ViewModel。我正在为每个人创建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]'。

使用Castle.Windsor

Factory Impl如下所示

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

注册:

container.AddFacility<TypedFactoryFacility>();

     ....

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

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

堆栈跟踪:

  

System.InvalidCastException未被用户代码
处理   HResult = -2147467262 Message =无法转换类型的对象   输入'Company.Namespace.SpecificViewModelBuilder'   'Company.Namespace.IStepModelBuilder`1 [Company.Namespace.IStepViewModel]'。   Source = DynamicProxyGenAssembly2 StackTrace:          在Castle.Proxies.IStepViewModelBuilderFactoryProxy.Create [T](T stepViewModel)          在Tests.Infrastructure.ViewModelBuilderFactoryTests.TestResolution()中   c:\ Project \ Infrastructure \ ViewModelBuilderFactoryTests.cs:第61行   的InnerException:

编辑:IModelBuilder<T>界面

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

2 个答案:

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

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

答案 1 :(得分:2)

我认为以下两个定义不兼容

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

public class SpecificViewModelBuilder : IStepModelBuilder<SpecificStepViewModel>
{
}

当Create运行时,它会将生成的类型(即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>();
    }
}

因此您可以专门化工厂,每个特定于一个SpecificStepViewModel