代码之家  ›  专栏  ›  技术社区  ›  JamesStuddart

if(obj是thisobj)语句太多

c#
  •  13
  • JamesStuddart  · 技术社区  · 14 年前

    我目前有一个方法,试图找出它接收到的对象是什么。它知道它在某个接口上,例如iService,但我有代码,它查看它并试图告诉我它是Service1还是Service2。 我目前有很多if(obj是thisobj)样式的语句,什么是使代码更漂亮的最佳解决方案?

    以下是我确切拥有的样本:

        public void DoSomething(IService service)
        {
            if (service is Service1)
            {
                //DO something
            }
    
            if (service is Service2)
            {
                //DO something else
            }           
        }
    

    现在有两个也不算太坏,但我想大概有20多个这样的东西,使用起来很糟糕。

    有什么想法吗?


    好的,我认为需要进一步的细节,这里是:

    在此方法之前,我还有另一个方法,它正在接收一个XML文档,它将XML文档反序列化到接口IService中,因此我们有如下内容:

        private static void Method(InnerXml)
    
        {
    
                var messageObj = (IServiceTask)XmlSerialization.Deserialize(typeof(IServiceTask), InnerXml);
    
                var service = GetService(messageObj);
                service.PerformTask(xmlDoc);
    
        }
    
        private static IService GetService(IServiceTask messageObj)
        {
            var service = new IService ();
    
            if (messageObj is Task1)
            {
                service = (SomeService)messageObj;
            }
    
            if (messageObj is Task2)
            {
                service = (SomeOtherService)messageObj;
            }
            return service ;
        }
    

    希望这能更清楚一点。

    12 回复  |  直到 14 年前
        1
  •  4
  •   Michael Lloyd Lee mlk    14 年前

    我相信你想要的是:

    class ServiceFactory 
    {
         Dictionary<Type, NewService> serviceCreators;
         ServiceFactory() 
         {
             serviceCreators = new Dictionary<Type, NewService>();
             serviceCreators.Add(typeof(Task1), delegate { return new SomeService(); });
             serviceCreators.Add(typeof(Task2), delegate { return new SomeOtherService(); });
         }
    
         public IService CreateService(IServiceTask messageObj) 
         {
             if(serviceCreators.Contains(messageObj.GetType()) 
             {
                  return serviceCreators[messageObj.GetType()];
             }
             return new DefaultService();
         }
    }
    
    delegate IService NewService();
    

    或者可以添加一个新方法 IServiceTask - CreateService .

        2
  •  16
  •   Itay Karo    14 年前

    你能换一下吗 IService ?

    添加方法 DoSomething() 并在所有服务中实现它。

        3
  •  13
  •   Jon Skeet    14 年前

    嗯,这取决于 //DO something 线在做。在某些情况下,在服务接口中声明一个方法并将这些操作的逻辑放在服务本身中是合适的。

    有时候,在另一方面,这是服务本身不应该知道的代码——此时,生活变得更加丑陋:(有时这是 分类 事情真的很难避免。我偶尔会发现泛型和lambda表达式的混合有帮助,例如

    ConditionallyExecute<Service1>(service, s1 => s1.CallSomeService1Method());
    ConditionallyExecute<Service2>(service, s2 => s2.CallSomeService2Method());
    ...
    

    哪里 ConditionallyExecute 类似于:

    private void ConditionallyExecute<T>(object obj, Action<T> action)
        where T : class
    {
        T t = obj as T;
        if (t != null)
        {
           action(t);
        }
    }
    

    …但我不是真的 幸福的 当我这样做的时候:(

        4
  •  9
  •   Will    14 年前

    我喜欢在这些场景中使用字典。

    Dictionary<Type,Action<IService>>
    
        5
  •  2
  •   antoni.rasul    14 年前

    至于我,我真的会同意 doSomething() 方法,以便您可以在所有这些类中实现它。你会有:

    public void DoSomething(IService service)
    {
    
        service.doSomething();
    }
    
        6
  •  1
  •   Oliver    14 年前

    这并不能使它更好地阅读,但可能会更好地执行(如果一个服务不能同时是两种类型):

        public void DoSomething(IService service)
        {
            if (service is Service1)
            {
                //DO something
            }
            else if (service is Service2)
            {
                //DO something else
            }           
        }
    

    另一种方法

    也许这也是一个可能的解决方案:

    private Dictionary<Type, Action<object>> _TypeExecutor;
    
    private void SetupExecutors()
    {
        _TypeExecutor = new Dictionary<Type, Action<object>>();
    
        _TypeExecutor.Add(typeof(Service1), new Action<object>((target) => target.DoSomething()));
        _TypeExecutor.Add(typeof(Service2), new Action<object>((target) =>
            {
                var instance = (Service2)target;
                var result = instance.DoSomething();
            }));
        _TypeExecutor.Add(typeof(Service3), AnotherMethod);
    
    }
    
    private void AnotherMethod(object target)
    {
        var instance = (Service3)target;
        var result = instance.DoSomething();
    }
    
    private void DoWork(ISomething something)
    {
        Action<object> action;
    
        if (_TypeExecutor.TryGetValue(something.GetType(), out action))
        {
            action(something);
        }
    }
    
        7
  •  1
  •   Thomas Weller    14 年前

    一般来说,如果您认为必须在代码中执行类似的操作,这是一个很强的迹象,表明您的设计有问题。如果你通过 IService 接口到方法,那么理想情况下,目的应该是它希望在该接口上调用一个方法,而不关心后面是什么实现!

    但除此之外。在你的情况下,有一些 Servicetype 您的财产 电子设备 接口(理想情况下,这将返回枚举值),然后可以使用 switch 声明。当然,这不会减少必要数量的逻辑分支(如果不重构体系结构,您将无法减少逻辑分支),但至少这会显著减少必要数量的代码。

    托马斯

        8
  •  1
  •   Community CDub    8 年前

    如果功能不属于 IService 那么要么 Will S Command pattern 以及某种类型的地图或使用 visitor pattern .

    后者要求您添加一个新方法 IService.Visit 并创建接口 IServiceVisitor 用这些方法 Visit(Service1) Visit(Service2) (等)

    例子:

    interface IService 
    {
        void Visit(IServiceVisitor visitor);
    }
    
    class Service1 : IService
    {
        void Visit(IServiceVisitor visitor) 
        {
            visitor.Visit(this);
        }
    }
    
    
    class Service2 : IService
    {
        void Visit(IServiceVisitor visitor) 
        {
            visitor.Visit(this);
        }
    }
    
    interface IServiceVisitor 
    {
        void Visit(Service1 service);
        void Visit(Service2 service);
    }
    
    class ClassThatDoesStuff : IServiceVisitor 
    {
    {
        void Visit(Service1 service) 
        {
             // Service one code
        }
        void Visit(Service2 service) 
        {
             // Service two code
        }
    
        public void DoSomething(IService service) 
        {
             serivce.Visit(this);
        }
    }
    
        9
  •  1
  •   Paul Nathan    14 年前

    使用多态性,这是一个非常简单的解决方案。

    class Abstract
    {
      function something();
    }
    
    class A inherit Abstract
    {
      override something()
    }
    
    class B inherit Abstract
    {
      override something()
    }
    
    
    function foo (Abstract input)
    {
     input->something()
    }
    
        10
  •  1
  •   Shadow Wizard    14 年前

    假设要根据实际类型执行某个方法,则可以对实例使用getmethod,如果该方法存在,则调用它。

    public void DoSomething(IService service)
    {
        System.Reflection.MethodInfo method = service.GetType().GetMethod("MySpecialMethod");
        if (method != null)
            method.Invoke(service, null);
    }
    

    这样您就不必检查类型了,只需检查方法是否存在——有点像在树上走动,所以我希望这种方法是有用的。

    您还可以使用可能的方法数组并对它们进行迭代,检查每一个方法并以这种方式使用更优雅的代码。

        11
  •  1
  •   Luke Hutteman    14 年前

    正如其他人所说,最简单的解决方案是通过添加的方法在iService实现内部完成此逻辑。

    但是,如果该功能确实不属于iService内部,那么访问者模式将是比大量检查实例更好的解决方案。

    您将创建一个类似

    public interface IServiceHandler {
        void handleService1(Service1 s);
        void handleService2(Service2 s);
        // add more methods for every existing subclass of IService
    }
    

    它的实现可以处理当前在剂量测量中的逻辑,但是每个分支都被分离成它自己的方法:

    public class ServiceHandler : IServiceHandler {
        public void handleService1(Service1 s) { ... }
        public void handleService2(Service2 s) { ... }
    }
    

    然后,IService需要一种添加方法:

    void accept(IServiceHandler sh);
    

    它将在特定的实现中实现,例如

    public class Service1 : IService {
        ...
        public void accept(IServiceHandler sh) { sh.handleService1(this); }
        ....
    }
    

    与其他实现类似。

    然后可以将原始dosomething()方法重写为

    public void DoSomething(IService service) {
        service.accept(new ServiceHandler());
    }
    

    这种方法的优点是,您的逻辑将更好地隔离,并且性能稍高,因为它不再使用任何检查或强制转换实例。

    另外,如果您添加了一个新的IService实现,编译器将强制您为它添加适当的处理程序(因为它需要实现accept()方法,这只能通过将适当的case添加到IServiceHandler来完成),而对于依赖于数字对于类型检查,很容易忘记添加适当的额外案例。

    最后但并非最不重要的是,如果您曾经需要任何其他类型的处理程序,那么您可以这样做而不需要对IService进行任何进一步的更改;您只需使用新的逻辑创建一个新的IServiceHandler实现。

        12
  •  0
  •   Mizipzor    14 年前

    如果您自己编写服务类,那么接口就是前进的道路。如果应该在对象上调用foo(),如果它是service1或service2,那么它们应该实现一个公共接口,您只需检查它是否是两者之一,然后运行相关的代码。

    但是,如果他们的课程不能改变,那么我认为你运气不好。20+完全不同的类,应该有20+完全不同的逻辑集应用到它们必须简单…以不同的方式处理。

    还是我错过了一些魔法?每次看到这样的代码,我都会想到如何实现接口。