استخدام الحالة/التبديل وGetType لتحديد الكائن [نسخة مكررة]

StackOverflow https://stackoverflow.com/questions/708911

سؤال

التكرار المحتمل:
C# - هل هناك بديل أفضل من هذا لـ "تشغيل النوع"؟

أذا أردت switch على نوع الكائن، ما هي أفضل طريقة للقيام بذلك؟

مقتطف الكود

private int GetNodeType(NodeDTO node)
{
    switch (node.GetType())
    { 
        case typeof(CasusNodeDTO):
            return 1;
        case typeof(BucketNodeDTO):
            return 3;
        case typeof(BranchNodeDTO):
            return 0;
        case typeof(LeafNodeDTO):
            return 2;
        default:
            return -1;
    }
}

أعلم أن هذا لا يسير بهذه الطريقة، لكنني كنت أتساءل كيف يمكنك حل هذه المشكلة.هو if/else البيان المناسب في هذه الحالة؟

أو هل تستخدم التبديل وإضافة .ToString() إلى النوع؟

هل كانت مفيدة؟

المحلول

اذا انا حقًا كان يجب ان switch على نوع الكائن، سأستخدمه .ToString().ومع ذلك، سأتجنب ذلك بأي ثمن: IDictionary<Type, int> سوف تفعل أفضل بكثير، زائر قد يكون الأمر مبالغة ولكنه بخلاف ذلك لا يزال حلاً جيدًا تمامًا.

نصائح أخرى

لن يؤدي هذا إلى حل مشكلتك بشكل مباشر لأنك تريد تشغيل الأنواع المحددة من قبل المستخدم، ولكن لصالح الآخرين الذين يريدون فقط تشغيل الأنواع المضمنة، يمكنك استخدام كود نوع تعداد:

switch (Type.GetTypeCode(node.GetType()))
{
    case TypeCode.Decimal:
        // Handle Decimal
        break;

    case TypeCode.Int32:
        // Handle Int32
        break;
     ...
}

في منشور مدونة MSDN كثير من الأسئلة:قم بتشغيل النوع هي بعض المعلومات عن السبب .شبكة لا يوفر التبديل على الأنواع.

كالعادة - الحلول موجودة دائمًا.

هذا ليس ملكي، ولكن لسوء الحظ فقدت المصدر.إنه يجعل التبديل بين الأنواع ممكنًا، لكنني شخصيًا أعتقد أنه أمر غريب جدًا (فكرة القاموس أفضل):

  public class Switch
  {
      public Switch(Object o)
      {
          Object = o;
      }

      public Object Object { get; private set; }
  }


  /// <summary>
  /// Extensions, because otherwise casing fails on Switch==null
  /// </summary>
  public static class SwitchExtensions
  {
      public static Switch Case<T>(this Switch s, Action<T> a)
            where T : class
      {
          return Case(s, o => true, a, false);
      }

      public static Switch Case<T>(this Switch s, Action<T> a,
           bool fallThrough) where T : class
      {
          return Case(s, o => true, a, fallThrough);
      }

      public static Switch Case<T>(this Switch s,
          Func<T, bool> c, Action<T> a) where T : class
      {
          return Case(s, c, a, false);
      }

      public static Switch Case<T>(this Switch s,
          Func<T, bool> c, Action<T> a, bool fallThrough) where T : class
      {
          if (s == null)
          {
              return null;
          }

          T t = s.Object as T;
          if (t != null)
          {
              if (c(t))
              {
                  a(t);
                  return fallThrough ? s : null;
              }
          }

          return s;
      }
  }

الاستخدام:

 new Switch(foo)
     .Case<Fizz>
         (action => { doingSomething = FirstMethodCall(); })
     .Case<Buzz>
         (action => { return false; })

سأستخدم فقط عبارة if.في هذه الحالة:

Type nodeType = node.GetType();
if (nodeType == typeof(CasusNodeDTO))
{
}
else ... 

الطريقة الأخرى للقيام بذلك هي:

if (node is CasusNodeDTO)
{
}
else ...

المثال الأول ينطبق على الأنواع المحددة فقط، حيث يتحقق الأخير من الميراث أيضًا.

أواجه نفس المشكلة ووجدت هذا المنشور.هل هذا هو المقصود من نهج IDictionary:

Dictionary<Type, int> typeDict = new Dictionary<Type, int>
{
    {typeof(int),0},
    {typeof(string),1},
    {typeof(MyClass),2}
};

void Foo(object o)
{
    switch (typeDict[o.GetType()])
    {
        case 0:
            Print("I'm a number.");
            break;
        case 1:
            Print("I'm a text.");
            break;
        case 2:
            Print("I'm classy.");
            break;
        default:
            break;
    }
}

إذا كان الأمر كذلك، فلا أستطيع أن أقول إنني من محبي التوفيق بين الأرقام الموجودة في القاموس وبيانات الحالة.

سيكون هذا مثاليًا ولكن مرجع القاموس يقتله:

void FantasyFoo(object o)
{
    switch (typeDict[o.GetType()])
    {
        case typeDict[typeof(int)]:
            Print("I'm a number.");
            break;
        case typeDict[typeof(string)]:
            Print("I'm a text.");
            break;
        case typeDict[typeof(MyClass)]:
            Print("I'm classy.");
            break;
        default:
            break;
    }
}

هل هناك تطبيق آخر أغفلته؟

انت تستطيع فعل ذالك:

if (node is CasusNodeDTO)
{
    ...
}
else if (node is BucketNodeDTO)
{
    ...
}
...

في حين أن ذلك سيكون أكثر أناقة، إلا أنه ربما لا يكون بنفس كفاءة بعض الإجابات الأخرى هنا.

انت تستطيع فعل ذالك:

function void PrintType(Type t) {
 var t = true;
 new Dictionary<Type, Action>{
   {typeof(bool), () => Console.WriteLine("bool")},
   {typeof(int),  () => Console.WriteLine("int")}
 }[t.GetType()]();
}

إنه واضح وسهل.إنه أبطأ قليلاً من تخزين القاموس مؤقتًا في مكان ما ..ولكن بالنسبة للكثير من التعليمات البرمجية، لن يكون هذا مهمًا على أي حال..

تتمثل إحدى الطرق في إضافة طريقة GetNodeType() افتراضية خالصة إلى NodeDTO وتجاوزها في الفروع بحيث يقوم كل فرع بإرجاع النوع الفعلي.

اعتماداً على ما تفعله في عبارة التبديل، فإن الإجابة الصحيحة هي تعدد الأشكال.ما عليك سوى وضع وظيفة افتراضية في فئة الواجهة/الأساسية وتجاوز كل نوع عقدة.

أنا في الواقع أفضل النهج المعطى كإجابة هنا:هل هناك بديل أفضل من هذا "لتشغيل النوع"؟

ومع ذلك، هناك حجة جيدة حول عدم تنفيذ أي أساليب مقارنة الأنواع في لغة موجهة للكائنات مثل C#.يمكنك كبديل توسيع وإضافة وظائف إضافية مطلوبة باستخدام الميراث.

تمت مناقشة هذه النقطة في تعليقات مدونة المؤلفين هنا:http://blogs.msdn.com/b/jaredpar/archive/2008/05/16/switching-on-types.aspx#8553535

لقد وجدت هذه نقطة مثيرة للاهتمام للغاية والتي غيرت أسلوبي في موقف مماثل وآمل فقط أن يساعد هذا الآخرين.

أطيب التحيات، واين

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top