c# - نوع التحقق: typeof أو GetType أو هو؟




(10)

لقد رأيت الكثير من الناس يستخدمون الكود التالي:

Type t = typeof(obj1);
if (t == typeof(int))
    // Some code here

لكنني أعلم أنه يمكنك أيضًا القيام بذلك:

if (obj1.GetType() == typeof(int))
    // Some code here

أو هذا:

if (obj1 is int)
    // Some code here

شخصياً ، أشعر أن الأخير هو أنظف ، لكن هل هناك شيء مفقود؟ أيهما أفضل استخدام ، أم أنه تفضيل شخصي؟


تستخدم للحصول على كائن System.Type لنوع. يأخذ التعبير typeof النموذج التالي:

System.Type type = typeof(int);

Example:

    public class ExampleClass
    {
       public int sampleMember;
       public void SampleMethod() {}

       static void Main()
       {
          Type t = typeof(ExampleClass);
          // Alternatively, you could use
          // ExampleClass obj = new ExampleClass();
          // Type t = obj.GetType();

          Console.WriteLine("Methods:");
          System.Reflection.MethodInfo[] methodInfo = t.GetMethods();

          foreach (System.Reflection.MethodInfo mInfo in methodInfo)
             Console.WriteLine(mInfo.ToString());

          Console.WriteLine("Members:");
          System.Reflection.MemberInfo[] memberInfo = t.GetMembers();

          foreach (System.Reflection.MemberInfo mInfo in memberInfo)
             Console.WriteLine(mInfo.ToString());
       }
    }
    /*
     Output:
        Methods:
        Void SampleMethod()
        System.String ToString()
        Boolean Equals(System.Object)
        Int32 GetHashCode()
        System.Type GetType()
        Members:
        Void SampleMethod()
        System.String ToString()
        Boolean Equals(System.Object)
        Int32 GetHashCode()
        System.Type GetType()
        Void .ctor()
        Int32 sampleMember
    */

يستخدم هذا النموذج أسلوب GetType لتحديد النوع المستخدم لاحتواء نتيجة حساب رقمي. هذا يعتمد على متطلبات التخزين للعدد الناتج.

    class GetTypeTest
    {
        static void Main()
        {
            int radius = 3;
            Console.WriteLine("Area = {0}", radius * radius * Math.PI);
            Console.WriteLine("The type is {0}",
                              (radius * radius * Math.PI).GetType()
            );
        }
    }
    /*
    Output:
    Area = 28.2743338823081
    The type is System.Double
    */

آخر واحد هو أنظف وأكثر وضوحا ، ويتحقق أيضا من أنواع فرعية. الآخرين لا تحقق من وجود تعدد الأشكال.


اختبار الأداء typeof () vs GetType ():

using System;
namespace ConsoleApplication1
    {
    class Program
    {
        enum TestEnum { E1, E2, E3 }
        static void Main(string[] args)
        {
            {
                var start = DateTime.UtcNow;
                for (var i = 0; i < 1000000000; i++)
                    Test1(TestEnum.E2);
                Console.WriteLine(DateTime.UtcNow - start);
            }
            {
                var start = DateTime.UtcNow;
                for (var i = 0; i < 1000000000; i++)
                    Test2(TestEnum.E2);
                Console.WriteLine(DateTime.UtcNow - start);
            }
            Console.ReadLine();
        }
        static Type Test1<T>(T value) => typeof(T);
        static Type Test2(object value) => value.GetType();
    }
}

النتائج في وضع التصحيح:

00:00:08.4096636
00:00:10.8570657

النتائج في وضع التحرير:

00:00:02.3799048
00:00:07.1797128

استخدم typeof عندما تريد الحصول على الكتابة في وقت التحويل البرمجي . استخدم GetType عندما تريد الحصول على النوع في وقت التنفيذ . نادرًا ما تكون هناك أي حالات لاستخدامها كما تفعل في المدلى بها ، وفي معظم الحالات ، ينتهي بك الأمر إلى وضع المتغير على أي حال.

هناك خيار رابع لم تفكر فيه (خاصة إذا كنت ستطرح كائنًا على النوع الذي تجده أيضًا) ؛ هذا هو استخدام.

Foo foo = obj as Foo;

if (foo != null)
    // your code here

هذا يستخدم فقط واحد يلقي في حين أن هذا النهج:

if (obj is Foo)
    Foo foo = (Foo)obj;

يتطلب اثنين .


كان لدي Type -property للمقارنة ولم أستطع استخدامه (مثل my_type is _BaseTypetoLookFor ) ، ولكن يمكنني استخدام هذه:

base_type.IsInstanceOfType(derived_object);
base_type.IsAssignableFrom(derived_type);
derived_type.IsSubClassOf(base_type);

لاحظ أن IsInstanceOfType و IsAssignableFrom بإرجاع true عند مقارنة نفس الأنواع ، حيث سيقوم بإرجاع IsSubClassOf false . و IsSubclassOf لا يعمل على الواجهات ، حيث تعمل الأخرى. (انظر أيضا هذا السؤال والجواب .)

public class Animal {}
public interface ITrainable {}
public class Dog : Animal, ITrainable{}

Animal dog = new Dog();

typeof(Animal).IsInstanceOfType(dog);     // true
typeof(Dog).IsInstanceOfType(dog);        // true
typeof(ITrainable).IsInstanceOfType(dog); // true

typeof(Animal).IsAssignableFrom(dog.GetType());      // true
typeof(Dog).IsAssignableFrom(dog.GetType());         // true
typeof(ITrainable).IsAssignableFrom(dog.GetType()); // true

dog.GetType().IsSubclassOf(typeof(Animal));            // true
dog.GetType().IsSubclassOf(typeof(Dog));               // false
dog.GetType().IsSubclassOf(typeof(ITrainable)); // false

كل شيء مختلف.

  • typeof يأخذ اسم نوع (الذي تحدده في وقت التحويل البرمجي).
  • يحصل GetType على نوع وقت التشغيل.
  • is إرجاع true إذا كان مثيل في شجرة الوراثة.

مثال

class Animal { } 
class Dog : Animal { }

void PrintTypes(Animal a) { 
    print(a.GetType() == typeof(Animal)) // false 
    print(a is Animal)                   // true 
    print(a.GetType() == typeof(Dog))    // true
}

Dog spot = new Dog(); 
PrintTypes(spot);

ماذا عن نوع typeof(T) ؟ هل تم حلها أيضًا في وقت التجميع؟

نعم فعلا. T هو دائماً ما يكون نوع التعبير. تذكر ، الطريقة العامة هي في الأساس مجموعة كاملة من الأساليب بالنوع المناسب. مثال:

string Foo<T>(T object) { return typeof(T).Name; }

Animal probably_a_dog = new Dog();
Dog    definitely_a_dog = new Dog();

Foo(probably_a_dog); // this calls Foo<Animal> and returns "Animal"
Foo<Animal>(probably_a_dog); // this is exactly the same as above
Foo<Dog>(probably_a_dog); // !!! This will not compile. The parameter expects a Dog, you cannot pass in an Animal.

Foo(definitely_a_dog); // this calls Foo<Dog> and returns "Dog"
Foo<Dog>(definitely_a_dog); // this is exactly the same as above.
Foo<Animal>(definitely_a_dog); // this calls Foo<Animal> and returns "Animal". 
Foo((Animal)definitely_a_dog); // this does the same as above, returns "Animal"

وأعتقد أن آخر واحد ينظر أيضا إلى الميراث (على سبيل المثال الكلب هو الحيوان == صحيح) ، وهو أفضل في معظم الحالات.


يمكنك استخدام عامل التشغيل typeof () في C # ولكن تحتاج إلى استدعاء مساحة الاسم باستخدام System.IO؛ يجب أن تستخدم الكلمة الرئيسية "is" إذا كنت ترغب في البحث عن نوع.


Type t = typeof(obj1);
if (t == typeof(int))
    // Some code here

هذا خطأ. يمكن أن يقوم عامل التشغيل typeof في C # فقط بأخذ أسماء النوع ، وليس الكائنات.

if (obj1.GetType() == typeof(int))
    // Some code here

هذا سوف يعمل ، ولكن ربما لا كما تتوقع. بالنسبة لأنواع القيم ، كما هو موضح هنا ، فهو مقبول ، ولكن بالنسبة لأنواع المراجع ، سيعود فقط إلى true إذا كان النوع هو نفس النوع بالضبط ، وليس شيئًا آخر في التسلسل الهرمي للوراثة. على سبيل المثال:

class Animal{}
class Dog : Animal{}

static void Foo(){
    object o = new Dog();

    if(o.GetType() == typeof(Animal))
        Console.WriteLine("o is an animal");
    Console.WriteLine("o is something else");
}

هذا من شأنه أن يطبع "o is something else" ، لأن نوع o هو Dog ، وليس Animal . يمكنك جعل هذا العمل ، ومع ذلك ، إذا كنت تستخدم أسلوب IsAssignableFrom من فئة IsAssignableFrom .

if(typeof(Animal).IsAssignableFrom(o.GetType())) // note use of tested type
    Console.WriteLine("o is an animal");

هذه التقنية لا تزال تترك مشكلة كبيرة ، على الرغم من. إذا كان المتغير الخاص بك فارغًا ، GetType() استدعاء GetType() إلى إلقاء NullReferenceException. لذلك لجعلها تعمل بشكل صحيح ، يمكنك القيام بما يلي:

if(o != null && typeof(Animal).IsAssignableFrom(o.GetType()))
    Console.WriteLine("o is an animal");

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

if(o is Animal)
    Console.WriteLine("o is an animal");

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

if(o is Animal)
    ((Animal)o).Speak();

ولكن هذا يجعل CLR يتحقق من نوع الكائن حتى مرتين. وسوف تتحقق مرة واحدة لإرضاء المشغل ، وإذا كان o الواقع Animal ، فإننا نجعله يتحقق مرة أخرى للتحقق من صحة المدلى بها.

إنه أكثر فعالية للقيام بذلك بدلاً من ذلك:

Animal a = o as Animal;
if(a != null)
    a.Speak();

يعتبر المشغل عبارة عن طاقم لن يلقي استثناءً إذا فشلت ، وبدلاً من ذلك يتم null . بهذه الطريقة ، يتحقق CLR من نوع الكائن مرة واحدة فقط ، وبعد ذلك ، نحتاج فقط إلى إجراء فحص فارغ ، وهو أكثر كفاءة.

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

(o as Animal).Speak();

في هذه الحالة ، يفترض المطور بوضوح أن o سيظل دائمًا Animal ، وطالما أن افتراضه صحيح ، فكل شيء يعمل بشكل جيد. ولكن إذا كانت خاطئة ، فإن ما ينتهي بهم الأمر هنا هو NullReferenceException . باستخدام طاقم Cast عادي ، سيكون بإمكانهم الحصول على InvalidCastException بدلاً من ذلك ، مما يعني تحديد المشكلة بشكل أكثر دقة.

في بعض الأحيان ، قد يكون من الصعب العثور على هذا الخطأ:

class Foo{
    readonly Animal animal;

    public Foo(object o){
        animal = o as Animal;
    }

    public void Interact(){
        animal.Speak();
    }
}

هذه حالة أخرى يتوقع فيها المطور بوضوح أن يكون Animal كل مرة ، ولكن هذا ليس واضحًا في المُنشئ ، حيث يتم استخدام المصبوب. ليس من الواضح حتى تصل إلى طريقة Interact ، حيث من المتوقع أن يتم تعيين حقل animal بشكل إيجابي. في هذه الحالة ، لا ينتهي بك الأمر فقط إلى استثناء مضلل ، ولكن لا يتم طرحه حتى يحتمل حدوثه في وقت متأخر عن وقت حدوث الخطأ الفعلي.

باختصار:

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

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

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


if (c is UserControl) c.Enabled = enable;



c#  

c#