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);
}
这里没有显示一个接口,那就是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
,而是可以传递IStepViewModel
或T
以外的任何东西,那么这应该可以解决您的问题:
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 一个