【问题标题】:ViewModelBuilder generics casting issue [duplicate]ViewModelBuilder 泛型铸造问题 [重复]
【发布时间】:2013-07-19 11:48:18
【问题描述】:

为此绞尽脑汁无济于事,想知道是否有人可以提供帮助?

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

提前致谢!

场景是一个向导站点的多个“步骤”视图模型。我正在为每个创建 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 实现如下工厂实现

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 消息=无法转换类型的对象 'Company.Namespace.SpecificViewModelBuilder' 键入 'Company.Namespace.IStepModelBuilder`1[Company.Namespace.IStepViewModel]'。 来源=DynamicProxyGenAssembly2 StackTrace: 在 Castle.Proxies.IStepViewModelBuilderFactoryProxy.Create[T](T stepViewModel) 在 Tests.Infrastructure.ViewModelBuilderFactoryTests.TestResolution() 中 c:\Project\Infrastructure\ViewModelBuilderFactoryTests.cs:第 61 行
内部异常:

编辑:IModelBuilder&lt;T&gt; 接口

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

【问题讨论】:

  • 看起来错误发生在该工厂的 Create 方法的实现中。请问可以提供吗?
  • 我只是在使用 Castle.Windsor 的 Typed factory impl - 我正在使用的过滤器选择器中添加。
  • 好的,如果可以的话,请添加异常的 .ToString() 转储。我想查看堆栈跟踪
  • 我从未使用过温莎城堡,但我确实在您的代码IStepModelBuilder&lt;TStepViewModel&gt; 中注意到了。那真的应该是TStepViewModel吗?
  • @AdiLester 谢谢我已经看到了这个问题 - 不知道它与这个具体有什么关系,或者至少我不能从中得到解决方案,有什么帮助吗?

标签: c# .net generics casting


【解决方案1】:

这里有一个界面你没有展示,那就是IModelBuilder&lt;T&gt;界面,但它是解决你问题的关键界面。

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

public interface IModelBuilder<T> { }

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

public interface IModelBuilder<out T> { }

out 修饰符使您的界面协变,这将允许您从IStepModelBuilder&lt;SpecificStepViewModel&gt; 转换为IStepModelBuilder&lt;IStepViewModel&gt;。您应该注意,这也对您的接口施加了一个约束,不允许它定义任何以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);
}

如果不是,那么你的尝试施法真的不应该被允许。

【讨论】:

  • 我也看到了这个,我会添加接口,但我认为我不能使用协方差,因为我也使用类型作为输入参数,因此就我而言知道它需要保持不变吗?
  • 谢谢你 - 真的很有用。所以在这种情况下 - 有没有办法重组整个事情以实现我想要实现的目标?
  • @M05Pr1mty 查看我的更新答案
  • 啊,我明白了——好吧,你是个英雄,因为这行得通,我必须将一个非特定类型的传入参数混为一谈,并在其中投射出来!再次感谢。
【解决方案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&lt;T&gt;

这个是不行的,你可以手动试试看:

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

【讨论】:

  • 我同意 - 你对如何解决这个问题有什么建议吗?要么我看这个太久了,要么它超出了我的范围!
  • 有趣的想法 - 我可能会在以后回来。也许某种抽象的工厂可以围绕它来提供正确的特定工厂......无论如何,谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-01-18
  • 2023-04-05
  • 1970-01-01
  • 2015-12-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多