ViewModelBuilder泛型转换问题

本文关键字:问题 转换 泛型 ViewModelBuilder | 更新日期: 2023-09-27 18:23:51

我绞尽脑汁,想知道是否有人能帮上忙?

遇到一个非常令人沮丧的选角问题,我肯定会很快得到答案,但可能只是因为我对泛型类型推理或其他方面的理解有限。

提前感谢!

场景是一个向导站点的多个"步骤"视图模型。我正在为每一个创建生成器类,并使用一个工厂来获取返回给我的步骤的特定生成器,这是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]'.

工厂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>()));

Stacktrace:

System.InvalidCastException未由用户代码处理
HResult=-2147467262消息=无法强制转换类型为的对象要键入的"Company.Namespace.SpecicViewModelBuilder"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:line 61
内部异常:

EDIT:IModelBuilder<T>接口

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

ViewModelBuilder泛型转换问题

这里没有显示一个接口,那就是IModelBuilder<T>接口,但它是解决问题的关键接口。

我假设它目前的定义是这样的

public interface IModelBuilder<T> { }

如果您使用自.NET4以来就可用的通用协方差,您将能够通过如下定义接口来解决问题:

public interface IModelBuilder<out T> { }

out修饰符使您的接口协变,这将允许您从IStepModelBuilder<SpecificStepViewModel>转换为IStepModelBuilder<IStepViewModel>。您应该注意,这也给您的接口设置了一个约束,不允许它定义任何将T作为参数的方法,而只能将其作为返回值。

你可以在这里阅读更多关于协方差和方差的信息。

编辑

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

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

如果不是将T作为参数传递给Create,而是可以传递IStepViewModelT以外的任何东西,那么这应该可以解决您的问题:

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

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

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

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 一个