转换继承到通用基础

时间:2013-05-02 18:01:00

标签: c# casting generics

有谁可以解释为什么switch语句中的返回转换不能在.net 4中编译?我已经更新了示例,以便更准确地了解我的情况。工厂本身并不是通用的。

如果我传入基础产品(实际上是StandardProduct),即使是“作为BaseProductProcessor”进行转换也不起作用。现在,如果我明确地将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
    {
    }
}

2 个答案:

答案 0 :(得分:3)

那是因为StandardProductProcessor期望类型为StandardProduct的对象。

在设计时,您只知道自己有Product

虽然每个StandardProduct都是Product,但不是相反。并非每个Product都是StandardProduct,这就是为什么您需要明确告诉编译器您有StandardProduct

答案 1 :(得分:1)

那么,你想在这里实现模板参数的协方差。基类不可能,但可以使用接口。因此,我建议您使用界面替换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     {         public 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#中的关键词),我推荐Eric Lippert的blog中的优秀系列(从底部开始)