如果(obj是thisObj)语句太多了

时间:2010-10-26 13:53:53

标签: c#

我目前有方法试图找出收到的obj是什么。它知道是在某个接口上,例如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 doc的方法,它将它们反序列化到接口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 个答案:

答案 0 :(得分:17)

您可以更改IService吗?

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

答案 1 :(得分:13)

嗯,这取决于//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);
    }
}

...当我这样做时,我并不是真的开心 :(

答案 2 :(得分:9)

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

Dictionary<Type,Action<IService>>

答案 3 :(得分:4)

我相信你想要的是:

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添加新方法。

答案 4 :(得分:2)

至于我 - 我真的会在接口上使用doSomething()方法,以便您可以在所有这些类中实现它。你会:

public void DoSomething(IService service)
{

    service.doSomething();
}

答案 5 :(得分:1)

这并没有使它更好阅读,但可能表现更好(如果服务不能同时是两种类型):

    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);
    }
}

答案 6 :(得分:1)

一般来说,如果您认为必须在代码中执行某些操作,则表明您的设计存在问题。如果你将IService接口传递给方法,那么理想的意图是它想要在该接口上调用一个方法 - 而不关心后面的实现是什么!

但除此之外。在你的情况下,在Servicetype接口上有一些IService属性可能是有用的(理想情况下这将返回一个枚举值),然后你可以使用switch语句进行检查。这当然不会减少必要数量的逻辑分支(如果不重构您的体系结构,您将无法减少它),但至少这会显着减少必要的代码量。

托马斯

答案 7 :(得分:1)

如果该功能不属于IService,那么Will s Command pattern和某种类型的地图或使用visitor pattern

后者要求您添加新方法IService.Visit并使用方法IServiceVisitorVisit(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);
    }
}

答案 8 :(得分:1)

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

class Abstract
{
  function something();
}

class A inherit Abstract
{
  override something()
}

class B inherit Abstract
{
  override something()
}


function foo (Abstract input)
{
 input->something()
}

答案 9 :(得分:1)

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

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

这样你根本不需要检查类型,只需检查方法是否存在 - 走遍树,所以我希望这种方法很有用。

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

答案 10 :(得分:1)

正如其他人所说的那样,最简单的解决方案就是通过添加的方法在IService实现中完成这个逻辑。

如果此功能确实不属于IService内部,则访问者模式将比大量的实例检查更好。

你会创建一个像

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

使用一个实现来处理当前DoSomething内部的逻辑,但每个分支都分成了它自己的方法:

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()方法,这只能通过添加适当的大小写来完成IServiceHandler也是如此,而依赖于大量类型检查的解决方案,很容易忘记添加适当的额外案例。

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

答案 11 :(得分:0)

如果您自己编写Service类,那么接口就是您的选择。如果要在对象上调用Foo(),如果它是Service1或Service2,那么它们应该实现一个公共接口,你只需检查它是否是其中之一,然后运行相关的代码。

然而,如果他们的班级无法改变,那么我认为你运气不好。 <+ p> .20个完全不同的类应该有20多个完全不同的逻辑集应用于它们

或者我在这里错过了一些C#魔术?每次我看到这样的代码时,我都会想到Go如何实现接口。