【问题标题】:Best way to do this generic abstract class in c#?在 C# 中做这个通用抽象类的最佳方法是什么?
【发布时间】:2012-03-30 19:36:58
【问题描述】:

我知道我做的不对,但我也知道有办法做到这一点。我试图尽可能地通用和抽象,否则我的代码会变得非常混乱。所以我在这里也使用了策略模式,也就是GetAggregateClient() 方法。

我想要一个名为AbstractAggregate<T> 的抽象类,以便它使用泛型。泛型类型将是一系列数据类(BlogItemResourceItemAskItem),它们都继承自 ListItem

这就是背景信息。

这里的问题是我希望GetAbstractAggregate() 返回实现AbstractAggregate 的客户端类之一的实例,并根据传入的枚举指定项目的类型。但是,我无法返回@987654329 @。编译器不会让我这样做,这是有道理的,因为 AbstractAggregateFactory 类不是泛型的。

有人知道最好的方法吗?

非常感谢。

public static class AggregateHelper
{
    public enum AggregateTypes { TankTruckBlog, AskTankTruck, Resources }
}

public static class AbstractAggregateFactory
{
    public static AbstractAggregate<T> GetAggregateClient(AggregateHelper.AggregateTypes type)
    {
        switch (type)
        {
            case AggregateHelper.AggregateTypes.AskTankTruck:
                return new AskTankTruckAggregate<AskItem>();
            case AggregateHelper.AggregateTypes.TankTruckBlog:
                return new TankTruckBlogAggregate<BlogItem>();
            case AggregateHelper.AggregateTypes.Resources:
                return new ResourcesAggregate<ResourceItem>();
            default:
                throw new AggregateDoesNotExistException();
        }
    }
}

public abstract class AbstractAggregate<T>
{
    public abstract List<T> GetAggregate(Guid[] resourcetypes);
    public abstract T GetSingle(string friendlyname);
}

public class AskTankTruckAggregate<T> : AbstractAggregate<T>
{
    // not implemented yet
}

public class TankTruckBlogAggregate<T> : AbstractAggregate<T>
{
    // not implemented yet
}

public class ResourcesAggregate<T> : AbstractAggregate<T>
{
    // not implemented yet
}

【问题讨论】:

    标签: c# generics refactoring abstract-class strategy-pattern


    【解决方案1】:

    编译器抱怨的问题是您有一个“开放”(T) 方法 - 并且您返回的是封闭的泛型(使用 &lt;AskItem&gt; 等),确实是具体类型。
    即你必须返回一个&lt;T&gt; - 你可以用方法做到这一点 - 无论工厂不是通用的,方法仍然可以。

    至于什么是最好的方法,这更像是一个设计问题,而且故事有点长。我不完全确定您要达到什么目的(可能是一些背景故事,您可能拥有多少种类型等)

    首先,您的项目不应该(一般来说,作为最佳实践或某些“感觉良好”的因素)继承自 ListItem。使用你的一些其他基类,如果你需要一个集合,使用一个通用的,比如List&lt;T&gt;,或者创建你自己的IList实现,等等。

    其次,您不需要将所有内容都通用化。您的基本聚合器是通用的,但自定义类通常不是。例如:

    abstract class ItemBase  { }
    class AskItem : ItemBase { }
    class BlogItem : ItemBase { }
    class ProvderA : ProviderBase<AskItem>
    {
        public override AskItem Get()
        {
            throw new NotImplementedException();
        }
    }
    class ProvderB : ProviderBase<BlogItem>
    {
        public override BlogItem Get()
        {
            throw new NotImplementedException();
        }
    }
    abstract class ProviderBase<T> where T : ItemBase
    {
        public abstract T Get();
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            ProviderBase<AskItem> provider = GetProvider<AskItem>();
            var item = provider.Get();
        }
        static ProviderBase<T> GetProvider<T>() where T : ItemBase
        {
            if (typeof(T) == typeof(AskItem))
                return (ProviderBase<T>)(object)new ProvderA();
            if (typeof(T) == typeof(BlogItem))
                return (ProviderBase<T>)(object)new ProvderB();
            return null;
        }
    }
    

    ...这是一种实现方式。

    基本上,使所有内容都“通用”并不总是最好的方法。您必须有足够的原因或未知的“类型”才能使用。与仿制药一样,您也需要付出一定的代价。将泛型跨越到非泛型世界通常很棘手,如果您的类型无法通过用法等推断出来,则涉及反射。

    在我看来,将每个提供程序都设为通用 (&lt;T&gt;) 是一个错误,因为它只接受一种类型(每个具体),而 base 是通用的。所以和上面一样。通常,泛型也受到每个接口的限制,无论您在哪里/在哪里。

    但是你有一个问题,因为从有效的非泛型类转换回泛型上下文不是直截了当的(还要记住,值类型有一些注意事项,因为你经常必须区别对待),反之亦然也是。

    因此你首先需要像 cast (object) 这样的东西。

    我宁愿在这里使用某种 IOC 方法 - 例如看看autofac(我没有关联,但我喜欢它的工作原理,不错的框架)。在这种情况下,你会做这样的事情:

    container.Register<ProviderBase<AskItem>>(c=> new ProvderA());
    container.Register<ProviderBase<BlogItem>>(c => new ProvderB());
    
    // and query later...
    
    ProviderBase<AskItem> provider = container.Resolve<ProviderBase<AskItem>>();
    

    希望这会有所帮助。

    【讨论】:

    • 感谢您的详细解释。我最初选择了一个不同的答案,但你说服了我。感谢您的帮助!
    • np :) - 有很多方法/答案,只要坚持保持逻辑(每一点通常都有其目的或不保证一个地方),你会没事的。看看 IOC,autofac,你会喜欢的(不仅仅是泛型:)
    • @CarenRose 感谢您的编辑(我的英语充满了错误),但要小心可能影响文本含义的更改(例如,在 Basically, making everything 'generic' is not always the best way 附近,您的文本完全错误地解释了它),我刚刚纠正了一些类似的事情。
    【解决方案2】:

    我不确定我是否理解您想要实现的目标,但也许是这样的

    public static class AbstractAggregateFactory
    {
        public static AbstractAggregate<T> GetAggregateClient<T>()
        {
            if(T is AskItem) return new AskTankTruckAggregate();
            if(T is BlogItem) return new TankTruckBlogAggregate();
            if(T is ResourceItem) return new ResourcesAggregate();
        }
    }
    
    public abstract class AbstractAggregate<T>
    {
        public abstract List<T> GetAggregate(Guid[] resourcetypes);
    
        public abstract T GetSingle(string friendlyname);
    }
    
    public class AskTankTruckAggregate : AbstractAggregate<AskItem>
    {
        //not implemented yet
    }
    
    public class TankTruckBlogAggregate : AbstractAggregate<BlogItem>
    {
        //not implemented yet
    }
    
    public class ResourcesAggregate : AbstractAggregate<ResourceItem>
    {
        //not implemented yet
    }
    

    【讨论】:

    • 我不认为你可以做 'T is BlogItem' - 你需要一个 T 的值 - 或 'typeof(T).Equals(typeof(BlogItem))' 或其他东西 - 就像一个旁注
    【解决方案3】:

    我试图尽可能地通用和抽象,否则我的代码会变得非常混乱。

    这是一种误解。通用/抽象实际上会使原本简单的问题复杂化。清洁代码的关键是封装。与继承或泛型大不相同。

    在这种情况下,我认为组合会是更好的选择,而不是继承。使用一组适配器,您可以拥有一个公共对象,每个实体都可以适应。例如:

    interface ICommon { ... }
    
    class AskAdaptor: ICommon
    {
        private readonly Ask ask;
        publick AskAdaptor(Ask ask)
        {
            this.ask = ask;
        }
    }
    
    class AskAdaptor: ICommon
    {
        private readonly Blog blog;
        publick AskAdaptor(Blog blog)
        {
            this.blog = blog;
        }
    }
    
    class AskAdaptor: ICommon
    {
        private readonly Resource resource;
        publick AskAdaptor(Resource resource)
        {
            this.resource = resource;
        }
    }
    
    class CommonAggregate
    {
        public void Add(ICommon common)
        {
             ....
        }
    }
    

    【讨论】:

      【解决方案4】:

      这个怎么样:

      public static class AggregateHelper
      {
          public enum AggregateTypes { TankTruckBlog, AskTankTruck, Resources }
      }
      
      public class AskItem { }
      public class BlogItem { }
      public class ResourceItem { }
      
      public static class AbstractAggregateFactory
      {
          public static AbstractAggregate<T> GetAggregateClient<T>
             (AggregateHelper.AggregateTypes type)
          {
              switch (type)
              {
                  case AggregateHelper.AggregateTypes.AskTankTruck:
                      return new AskTankTruckAggregate<T>();
                  case AggregateHelper.AggregateTypes.TankTruckBlog:
                      return new TankTruckBlogAggregate<T>();
                  case AggregateHelper.AggregateTypes.Resources:
                      return new ResourcesAggregate<T>();
                  default:
                      throw new ArgumentException();
              }
          }
      }
      
      public abstract class AbstractAggregate<T>
      {
          public abstract List<T> GetAggregate(Guid[] resourcetypes);
          public abstract T GetSingle(string friendlyname);
      }
      
      public class AskTankTruckAggregate<T> : AbstractAggregate<T>
      {
          public override List<T> GetAggregate(Guid[] resourcetypes)
          {
              throw new NotImplementedException();
          }
      
          public override T GetSingle(string friendlyname)
          {
              Console.WriteLine(friendlyname);
              Type whats_t = typeof(T);
              return default(T);
          }
      }
      
      public class TankTruckBlogAggregate<T> : AbstractAggregate<T>
      {
          //not implemented yet
      }
      
      public class ResourcesAggregate<T> : AbstractAggregate<T>
      {
          //not implemented yet
      }
      

      例子:

      AbstractAggregate<BlogItem> foo3 = 
         AbstractAggregateFactory.GetAggregateClient<BlogItem>(AggregateHelper.AggregateTypes.AskTankTruck);
      foo3.GetSingle("test");
      

      【讨论】:

        【解决方案5】:

        可能清楚的一点是你的设计有些缺陷。在通用方法中,打开类型并不是最好的做法,因为它违背了它的目的。但是不清楚的是您的课程的目的是什么。

        一些猜测:

        1) 看到你的配对类AskItemAskTankTruckAggregate&lt;T&gt; 等我不认为后者必须是一个通用类,它是一个非常具体的类,与AskItem 紧密耦合。我会像这样重新设计它

        public static class AbstractAggregateFactory
        {
            public static AbstractAggregate<T> GetAggregateClient<T>() where T : ListItem
            {
                //use reflection to find the type that inherits AbstractAggregate<T>
        
                //instantiate the type
        
                //cast to AbstractAggregate<T> and return
            }
        }
        
        public class AskTankTruckAggregate : AbstractAggregate<AskItem>
        {
            //not implemented yet
        }
        
        public class TankTruckBlogAggregate : AbstractAggregate<BlogItem>
        {
            //not implemented yet
        }
        
        public class ResourcesAggregate : AbstractAggregate<ResourceItem>
        {
            //not implemented yet
        }
        

        这样称呼:

        AbstractAggregateFactory.GetAggregateClient<AskItem>(); //etc
        

        2) 另一种方式:将聚合创建作业委托给您的 ListItems。

        public abstract class ListItem //or interface
        {
            protected abstract object Create();
        } 
        public class AskItem : ListItem { //implement to return AskTankTruckAggregate 
        }
        public class BlogItem : ListItem { //implement to return TankTruckBlogAggregate 
        }
        public class ResourceItem : ListItem { //implement to return ResourcesAggregate 
        }
        
        public static class AbstractAggregateFactory
        {
            public static AbstractAggregate<T> GetAggregateClient<T>() where T : ListItem, new()
            {
                return (AbstractAggregate<T>)new T().Create();
            }
        }
        
        public class AskTankTruckAggregate : AbstractAggregate<AskItem>
        {
            //not implemented yet
        }
        
        public class TankTruckBlogAggregate : AbstractAggregate<BlogItem>
        {
            //not implemented yet
        }
        
        public class ResourcesAggregate : AbstractAggregate<ResourceItem>
        {
            //not implemented yet
        }
        

        这样称呼:

        AbstractAggregateFactory.GetAggregateClient<AskItem>(); //etc
        

        3) 或相同,但使用泛型使其类型更强:

        public abstract class ListItem<T> where T : ListItem<T> //or interface
        {
            protected abstract AbstractAggregate<T> Create();
        } 
        public class AskItem : ListItem<AskItem> { //implement to return AskTankTruckAggregate 
        }
        public class BlogItem : ListItem<BlogItem> { //implement to return TankTruckBlogAggregate 
        }
        public class ResourceItem : ListItem<ResourceItem> { //implement to return ResourcesAggregate 
        }
        
        public static class AbstractAggregateFactory
        {
            public static AbstractAggregate<T> GetAggregateClient<T>() where T : ListItem, new()
            {
                return new T().Create();
            }
        }
        
        public class AskTankTruckAggregate : AbstractAggregate<AskItem>
        {
            //not implemented yet
        }
        
        public class TankTruckBlogAggregate : AbstractAggregate<BlogItem>
        {
            //not implemented yet
        }
        
        public class ResourcesAggregate : AbstractAggregate<ResourceItem>
        {
            //not implemented yet
        }
        

        这样称呼:

        AbstractAggregateFactory.GetAggregateClient<AskItem>(); //etc
        

        4) 最后,可能会使返回类型不那么通用吗?涉及switch case,我不喜欢。

        public enum AggregateTypes { TankTruckBlog, AskTankTruck, Resources }
        
        public static class AbstractAggregateFactory
        {
            public static AbstractAggregate GetAggregateClient(AggregateTypes type)
            {
                switch (type)
                {
                    case AggregateTypes.AskTankTruck:
                        return new AskTankTruckAggregate<AskItem>();
                    case AggregateTypes.TankTruckBlog:
                        return new TankTruckBlogAggregate<BlogItem>();
                    case AggregateTypes.Resources:
                        return new ResourcesAggregate<ResourceItem>();
                    default:
                        throw new AggregateDoesNotExistException();
                }
            }
        }
        
        public abstract class AbstractAggregate
        {
        
        }
        
        public abstract class AbstractAggregate<T> : AbstractAggregate
        {
        
        }
        
        //or change the definition to AskTankTruckAggregate : AbstractAggregate<AskItem>
        public class AskTankTruckAggregate<T> : AbstractAggregate<T>
        {
            //not implemented yet
        }
        
        //or change the definition to TankTruckBlogAggregate : AbstractAggregate<BlogItem>
        public class TankTruckBlogAggregate<T> : AbstractAggregate<T>
        {
            //not implemented yet
        }
        
        //or change the definition to ResourcesAggregate : AbstractAggregate<ResourceItem>
        public class ResourcesAggregate<T> : AbstractAggregate<T>
        {
            //not implemented yet
        }
        

        这样称呼:

        AbstractAggregateFactory.GetAggregateClient(AggregateTypes.AskTankTruck); //etc
        

        Imo,这种方法比反射方法更糟糕。以后很容易忘记一些枚举检查。


        总而言之,3rd 在我看来是最好的,但在不知道你的设计目标的情况下,它很难预测。几点建议:

        1. 您的工厂名称听起来更像AggregateFactory。其中的“抽象”使它更多地关注实现。

        2. 如果你需要一个枚举来表示类型,不要让它嵌套。嵌套的公共类型更难调用。取出包装静态类(如我的第 5 种方法)。

        3. 将您的基类重命名为Aggregate&lt;T&gt;AggregateBase&lt;T&gt;。再次,它中的“抽象”使它更多地与实现有关,完全没有必要。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2021-08-07
          • 2021-08-31
          • 1970-01-01
          • 1970-01-01
          • 2011-04-10
          • 1970-01-01
          • 2010-09-18
          相关资源
          最近更新 更多