2013-05-02 135 views
1

任何人都可以解释为什么在switch语句的返回中的转换不能在.net 4中编译?我已经更新了这个例子,以便更准确地了解我的情况。工厂本身实际上不是通用的。convert inherited to generic base

即使铸造“as BaseProductProcessor”也不起作用如果我传入基础产品(实际上是StandardProduct)。现在,如果我明确地将StandardProduct类型传递给工厂,那么就可以了 - 但是我已经定义的是所有调用方法中的Product类型:

如何解决这个问题?

using System; 
using Microsoft.VisualStudio.TestTools.UnitTesting; 

namespace testing 
{ 
    [TestClass] 
    public class Test 
    { 
     [TestMethod]//fails 
     public void TestFactoryMethodWithBaseTypePassed() 
     { 
      Product product = new testing.StandardProduct(); 
      var pp = new testing.ProductProcessorFactory().Create(product); 
      Assert.IsNotNull(pp);//fails because T coming into create wasn't the derived type 
     } 
     [TestMethod]//passes 
     public void TestFactoryMethodWithExactType() 
     { 
      var pp = new testing.ProductProcessorFactory().Create(new testing.StandardProduct()); 
      Assert.IsNotNull(pp); 
     } 
    } 
    public abstract class BaseProductProcessor<T> where T : Product 
    { 
     public T Product { get; set; } 
     public BaseProductProcessor(T product) 
     { 
      Product = product; 
     } 
    } 

    public class StandardProductProcessor : BaseProductProcessor<StandardProduct> 
    { 
     public StandardProductProcessor(StandardProduct product) 
      : base(product) 
     { 
     } 
    } 

    public class ProductProcessorFactory 
    { 
     public ProductProcessorFactory() 
     { 
     } 

     public BaseProductProcessor<T> Create<T>(T product) where T : Product 
     { 
      switch (product.ProductType) 
      { 
       case ProductType.Standard: 
        var spp = new StandardProductProcessor(product as StandardProduct); 
        return spp as BaseProductProcessor<T>;//Nulls if T passed with a Product.. how to explicitly say T is a StandardProduct right here in the factory method so it's centralized? 
      } 
      return null;// spp as BaseProductProcessor<T>; 
     } 
    } 

    public class Product 
    { 
     public ProductType ProductType { get; set; } 
    } 

    public enum ProductType 
    { 
     Standard, 
     Special 
    } 

    public class StandardProduct : Product 
    { 
    } 
} 
+0

如果有人创建了一个'ProductProcessorFactory会发生什么'?你可以创建与'BaseProductProcessor '兼容的东西,然后尝试将其转换为'BaseProductProcessor ',这会给你一个ClassCastException。编译器试图告诉你,这个转换不会在所有'T's成功。 – cdhowie 2013-05-02 18:04:57

+0

更新的代码 - 工厂不是通用的 – 2013-05-03 10:04:04

+0

但方法是,所以你会有同样的问题。 – cdhowie 2013-05-03 16:14:43

回答

1

那么,在这里你想实现模板参数的协方差。这对于基类是不可能的,但它可能与intefaces有关。所以,我建议你用接口来取代你abstract class BaseProductProcessor<T>

public interface IBaseProductProcessor<out T> where T : Product // out marks argument as covariant 
{ 
    T Product { get; } // absense of setter is crusial here - otherwise you'll violate type safety 
} 

StandartProcessor:

public class StandardProductProcessor : IBaseProductProcessor<StandardProduct> 
{ 
    public StandardProductProcessor(StandardProduct product) 
    { 
     Product = product; 
    } 

    public StandardProduct Product { get; private set; } 
} 

以及与此,只需要修改你的工厂函数如下: 公共类ProductProcessorFactory { 公共ProductProcessorFactory( ) { }

public IBaseProductProcessor<T> Create<T>(T product) where T : Product 
    { 
     switch (product.ProductType) 
     { 
      case ProductType.Standard: 
       var spp = new StandardProductProcessor(product as StandardProduct); 
       return spp as IBaseProductProcessor<T>;//no more nulls! 
     } 
     return null; 
    } 
} 

通过此修改,您的两个测试都会通过。

如果您想了解更多关于协变和逆变(并且于C#的关键字),我建议在埃里克利珀的blog佳系列(与底部的人开始)

+0

好的!!!好的信息,谢谢。我确实看到它的工作原理,但我需要基本的通用代码:|总是有取舍,呵呵? :) 我最终把一个的调用方法和argEx如果T是一个基地,并没有太多头痛。 – 2013-05-03 17:46:42

3

这是因为StandardProductProcessor预计StandardProduct类型的对象。

在设计时你只知道你有一个Product

尽管每个StandardProductProduct,但这并不是相反。不是每个Product都是StandardProduct,这就是为什么你需要明确地告诉编译器你有一个StandardProduct

+0

你能帮助更多吗?这个回应并没有解决我的问题。演员阵容最好。这是处理器的表演。 – 2013-05-03 10:43:38