如何避免拳击和丑陋的switch语句 [英] How to avoid boxing and ugly switch statements

查看:88
本文介绍了如何避免拳击和丑陋的switch语句的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我们有以下内容:

  object  [] objs =  new   object  [ 3 ] { 最终问题,42L, 1 }; 



我们想对数组中的各个对象执行某些操作,例如:

  foreach  object  o  in  objs)
{
dosomething(o);
}



现在 dosomething 只需对象参数所以有拳击正在进行,但我们要求能够根据对象的类型做一些事情,所以我们会做类似的事情:

  public   void  dosomething( object  obj)
{
// 单向
if (obj.GetType()== typeof string ))
{
// 字符串特定内容
}
// 另一种方式
if (obj long
{
// 长特定内容
}

}



嗯,这是丑陋和不高效的,是否有更好的方式使用泛型等?

解决方案

首先,as您的数组已经被键入为对象,在构建数组时会发生装箱。从数组中读取值不会进行新的装箱(它可以取消装箱)。



要测试类型,最好使用运算符。永远不要使用GetType()或GetType()进行比较.Name / FullName。



但是,这一切都与目的有关。

为什么要将所有值放在数组中?

如果您需要在数组或列表中使用它们,比如相应的查看器(类似于数据模板)可以查看的通用数据,那么您可以避免装箱。



如果你想要处理数据类型并且可以配置,你可以创建一个词典。类似于:

  var  dictionary =  new  Dictionary< Type,Action< object>>(); 

// 然后您注册所需的所有类型:
dictionary.Add( typeof int ),MethodToDealWithInt32);
dictionary.Add( typeof string ),MethodToDealWithString);

// 然后,当你得到一个值时,你称之为:
public void 调用( object value
{
if value == null
{
DoNullAction();
return ;
}

操作< object> action = dictionary [ value .GetType()];
// 如果没有为该类型注册操作,则会抛出异常。
action( value );
}





这样,您可以为任何新类型注册相应的操作。当然,您需要在应用程序中的某个时间执行此操作,但是Call()的调用者不需要对有效处理该对象的代码有任何强引用。


取决于你想要什么。无论如何,使用对象来存储任何东西看起来都是一个坏主意。

如果你使用泛型,你会有像List< Object>这样的东西。产生相同的结果。

你有什么似乎是'上帝方法 [< a href =http://en.wikipedia.org/wiki/God_objecttarget =_ blanktitle =New Window> ^ ]'可以根据输入做各种不同的事情。

一种方法可能是拥有一个具有Property Object和Method DoSomething的接口。这样你就可以为每种类型写一个Class和一个特定的DoSomething。

我想一个实现看起来像这样:

 < span class =code-keyword> public   interface  IDoSomethingWithObj 
{
object TheObject { get ; set ; }
void DoSomething();
}

public class DoSomethingWithString:IDoSomethingWithObj
{
object IDoSomething.TheObject { get ; set ; } // 测试对象是否为字符串,否则抛出异常。
< span class =code-sdkkeyword> String MyString { get ; set ; } // 在此设置TheObject。排序'假货'类型安全。
void DoSomething(){ // 使用String执行某些操作。 }
}



另外,可能在构造函数中设置String(接口中没有MyString)。

现在你可以有一个IDoSomethingWithObj的列表或数组。

  foreach (IDoSomethingWithObj obj  myList中的class =code-keyword> 
{obj.DoSomething}



我想这是处理这种情况的一种方法(可能不是最好的,但比我不得不做出巨大的切换语句更好。)

缺点是你不能用其他对象替换对象(如果你想),因为这是更多或者更少'类型安全',所以你必须确保新的对象类型与旧的匹配。



另一种方法,如果你知道你想要什么(如同一个字符串,int,long,bool,你可以使用元组 [ ^ ]。

这里的缺点是你不知道'Item1'之类的属性是什么'Item8'可能意味着。



不错的问题。也对其他解决方案感兴趣:)


我在文章动态方法调度程序,没有更长的转换语句!



-SA

Say we have the following :

object[] objs = new object[3]{ "the ultimate question", 42L, 1}; 


and we want to do something to the individual objects in the array like :

foreach (object o in objs)
{
   dosomething(o);
}


Now dosomething will only take object parameters so there is boxing going on, but we require to be able to do something based on the type of the object, so we would do something like :

public void dosomething(object obj)
{ 
   // one way
   if(obj.GetType() == typeof(string))
   {
      // string specific something
   }
   // another way
   if(obj is long)
   {
      // long specific something
   }
   
}


Well it's ugly and non performant, is there a better way possible using generics etc.?

解决方案

First, as your array is already typed as object, the boxing occurs when building the array. Reading the values from the array does not do a new boxing (it can unbox it).

To test for types, it is better to use the is operator. Never compare using GetType() or GetType().Name/FullName.

But then, it is all related to the purpose.
Why are you putting all the values in an array?
If you need them in an array or list, like a generic data to be viewed by the appropriate viewer (similar to Data-Templates) then you can't avoid boxing.

If you want something to deal with datatypes and that can be configurable, you can create a Dictionary. Something like:

var dictionary = new Dictionary<Type, Action<object>>();

// Then you register all the types you want like:
dictionary.Add(typeof(int), MethodToDealWithInt32);
dictionary.Add(typeof(string), MethodToDealWithString);

//Then, when you get a value, you call it like:
public void Call(object value)
{
  if (value == null)
  {
    DoNullAction();
    return;
  }

  Action<object> action = dictionary[value.GetType()];
  // this will thrown an exception if there is no action registered for the type.
  action(value);
}



With that, you can register the appropriate action for any new type. Surely you will need to do that at some time in your application, but then the caller of "Call()" does not need to have any strong reference to the code that effectively deals with the object.


Depends on what you want. Using Objects to store just about anything looks like a bad idea anyway.
If you'd work with Generics you'd have something like List<Object> which yields the same result.
What you have there seems like a 'God Method[^]' that can do all sorts of different things dependent on its input.
One way perhaps is to have an Interface that has a Property Object and a Method DoSomething. That way you could write a Class and a specific DoSomething for each type.
I imagine an implementation would look something like this:

public interface IDoSomethingWithObj
{
    object TheObject { get; set; }
    void DoSomething();
}

public class DoSomethingWithString : IDoSomethingWithObj
{
    object IDoSomething.TheObject { get; set; } // Test if the object is a string here, otherwise throw an Exception.
    String MyString { get; set; } // Set TheObject here. Sort of 'fakes' type safety.
    void DoSomething() { // Do something with the String. }
}


Also, possibly set the String in the Constructor (MyString isn't available in the Interface).
Now you could have a list or array of IDoSomethingWithObj.

foreach (IDoSomethingWithObj obj in myList)
{ obj.DoSomething }


I guess this is one way of handling such a situation (may not be the best, but better than having to make huge switch statements I guess).
The downside is that you cannot replace the Objects with other Objects (if you wanted to) because this is more or less 'type safe' so you'd have to make sure the new Objects type matched the old.

Another Method, if you know what you want (like always a String, int, long, bool in that order) you could use a Tuple[^].
The downside here is that you have no idea what Properties like 'Item1' up to 'Item8' could possible mean.

Nice question. Interested in other solutions too :)


I offer a comprehensive solution in my article "Dynamic Method Dispatcher, No more long switch statements!".

—SA


这篇关于如何避免拳击和丑陋的switch语句的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆