Pengecekan Jenis: typeof, GetType, atau is?

1513

Saya telah melihat banyak orang menggunakan kode berikut:

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

Tapi saya tahu Anda juga bisa melakukan ini:

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

Atau ini:

if (obj1 is int)
    // Some code here

Secara pribadi, saya merasa yang terakhir adalah yang terbersih, tetapi apakah ada sesuatu yang saya lewatkan? Mana yang terbaik untuk digunakan, atau preferensi pribadi?

jasonh
sumber
28
Jangan lupa as!
RCIX
82
astidak benar-benar memeriksa jenis meskipun ...
jasonh
49
astentu saja merupakan bentuk pengecekan tipe, setiap bit sebanyak isitu! Ini secara efektif menggunakan di isbelakang layar, dan digunakan di semua tempat di MSDN di tempat-tempat di mana ia meningkatkan kebersihan kode versus is. Alih-alih memeriksa isterlebih dahulu, panggilan untuk asmembuat variabel yang diketik siap digunakan: Jika nol, jawab dengan tepat; jika tidak, lanjutkan. Tentu saja sesuatu yang saya lihat dan gunakan sedikit.
Zaccone
15
Ada perbedaan kinerja yang signifikan dalam mendukung as/ is(tercakup dalam stackoverflow.com/a/27813381/477420 ) dengan asumsi semantiknya berfungsi untuk kasing Anda.
Alexei Levenkov
@ Samusarin itu tidak "menggunakan" refleksi. ItuGetType Metode Anda terhubung ke dalam System.Reflection.Assembly- metode yang sama sekali berbeda dan tidak relevan di sini.
Kirk Woll

Jawaban:

1849

Semua berbeda.

  • typeof membutuhkan nama tipe (yang Anda tentukan pada waktu kompilasi).
  • GetType mendapatkan tipe runtime dari sebuah instance.
  • is mengembalikan true jika turunan ada di pohon warisan.

Contoh

class Animal { } 
class Dog : Animal { }

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

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

Bagaimana dengan typeof(T)? Apakah ini juga diselesaikan pada saat kompilasi?

Iya. T selalu seperti apa jenis ekspresinya. Ingat, metode generik pada dasarnya adalah sejumlah metode dengan tipe yang sesuai. Contoh:

string Foo<T>(T parameter) { 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"
Jimmy
sumber
29
Ah, jadi jika saya memiliki kelas Ford yang berasal dari Mobil dan turunan dari Ford, memeriksa "adalah Mobil" pada contoh itu akan benar. Masuk akal!
jasonh
2
Untuk memperjelas, saya tahu itu, tapi saya berkomentar sebelum Anda menambahkan sampel kode. Saya ingin mencoba menambahkan kejelasan bahasa Inggris sederhana ke jawaban Anda yang sudah sangat baik.
jasonh
12
@ Kimmy jika typeof dievaluasi pada waktu kompilasi dan GetType () dievaluasi pada saat runtime, maka masuk akal bahwa GetType () menimbulkan sedikit hit performa
Cedric Mamo
bagaimana dengan Anjing baru (). GetType () adalah Hewan Atau tipe (Anjing) adalah Hewan, itu hanya memberi peringatan dan bukan kesalahan?
Prerak K
7
@ PrerakK new Dog().GetType() is Animalmengembalikan false (dan versi Anda yang lain juga) karena .GetType()mengembalikan objek bertipe Type, dan Typebukan Animal.
Maarten
195

Gunakan typeofsaat Anda ingin mendapatkan jenis pada waktu kompilasi . Gunakan GetTypesaat Anda ingin mendapatkan jenis pada waktu eksekusi . Jarang ada kasus untuk digunakan iskarena melakukan pemeran dan, dalam kebanyakan kasus, Anda akhirnya casting variabel.

Ada opsi keempat yang belum Anda pertimbangkan (terutama jika Anda akan melemparkan objek ke jenis yang Anda temukan juga); itu untuk digunakan as.

Foo foo = obj as Foo;

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

Ini hanya menggunakan satu pemeran sedangkan pendekatan ini:

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

membutuhkan dua .

Pembaruan (Jan 2020):

  • Mulai dari C # 7+ , Anda sekarang dapat menggunakan inline, jadi pendekatan 'is' sekarang dapat dilakukan dalam satu pemeran juga.

Contoh:

if(obj is Foo newLocalFoo)
{
    // For example, you can now reference 'newLocalFoo' in this local scope
    Console.WriteLine(newLocalFoo);
}
Andrew Hare
sumber
4
Dengan perubahan dalam. NET 4 ismasih melakukan gips?
ahsteele
6
Apakah jawaban ini benar? Benarkah Anda benar-benar dapat mengirimkan instance ke typeof ()? Pengalaman saya tidak. Tapi saya kira itu umumnya benar bahwa memeriksa sebuah instance mungkin harus terjadi pada saat runtime, sedangkan memeriksa kelas harus dapat dilakukan pada waktu kompilasi.
Jon Coombs
4
@jon (4 tahun setelah q Anda.), tidak, Anda tidak dapat mengirimkan instance ke typeof(), dan jawaban ini tidak menyarankan Anda bisa. Anda memasukkan jenis sebagai gantinya, yaitu, typeof(string)bekerja, typeof("foo")tidak.
Abel
Saya tidak percaya ismelakukan pemeran, operasi yang agak khusus di IL.
abatishchev
3
Kita sekarang dapat melakukanif (obj is Foo foo) { /* use foo here */ }
Ivan García Topete
71

1.

Type t = typeof(obj1);
if (t == typeof(int))

Ini ilegal, karena typeofhanya berfungsi pada tipe, bukan pada variabel. Saya menganggap obj1 adalah variabel. Jadi, dengan cara typeofini statis, dan tidak berfungsi pada waktu kompilasi alih-alih runtime.

2.

if (obj1.GetType() == typeof(int))

Ini truejika obj1persis tipe int. Jika obj1berasal dari int, kondisi if akan false.

3.

if (obj1 is int)

Ini adalah truejika obj1adalah int, atau jika berasal dari kelas yang disebut int, atau jika mengimplementasikan antarmuka yang disebut int.

Scott Langham
sumber
Berpikir tentang 1, Anda benar. Namun, saya sudah melihatnya di beberapa contoh kode di sini. Itu harus Ketik t = obj1.GetType ();
jasonh
4
Ya, saya kira begitu. "typeof (obj1)" tidak dapat dikompilasi ketika saya mencobanya.
Scott Langham
4
Tidak mungkin diturunkan dari System.Int32 atau tipe nilai lainnya di C #
reggaeguitar
dapatkah Anda memberi tahu apa yang akan menjadi typeof (typeof (system.int32))
Sana
1
@ Sana, mengapa Anda tidak mencobanya :) Saya akan membayangkan meskipun Anda mendapatkan kembali contoh System.Type yang mewakili tipe System.Type! Dokumentasi untuk typeof ada di sini: docs.microsoft.com/en-us/dotnet/csharp/language-reference/…
Scott Langham
53
Type t = typeof(obj1);
if (t == typeof(int))
    // Some code here

Ini adalah kesalahan. Operator typeof di C # hanya bisa mengambil nama tipe, bukan objek.

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

Ini akan berhasil, tetapi mungkin tidak seperti yang Anda harapkan. Untuk tipe nilai, seperti yang Anda tunjukkan di sini, itu dapat diterima, tetapi untuk tipe referensi, itu hanya akan mengembalikan true jika jenisnya adalah tipe yang sama persis , bukan sesuatu yang lain dalam hierarki warisan. Contohnya:

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

Ini akan mencetak "o is something else", karena jenisnya oadalah Dog, bukan Animal. Anda dapat membuat ini berfungsi, jika Anda menggunakan IsAssignableFrommetode Typekelas.

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

Teknik ini masih menyisakan masalah besar. Jika variabel Anda nol, panggilan ke GetType()akan melempar NullReferenceException. Jadi untuk membuatnya bekerja dengan benar, Anda harus:

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

Dengan ini, Anda memiliki perilaku iskata kunci yang setara . Karenanya, jika ini adalah perilaku yang Anda inginkan, Anda harus menggunakan iskata kunci, yang lebih mudah dibaca dan lebih efisien.

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

Namun, dalam kebanyakan kasus, iskata kunci masih bukan yang Anda inginkan, karena biasanya tidak cukup hanya untuk mengetahui bahwa suatu objek adalah tipe tertentu. Biasanya, Anda ingin benar-benar menggunakan objek itu sebagai turunan dari tipe itu, yang memerlukan casting juga. Jadi Anda mungkin menemukan diri Anda menulis kode seperti ini:

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

Tapi itu membuat CLR memeriksa jenis objek hingga dua kali. Ini akan memeriksa sekali untuk memuaskan isoperator, dan jika omemang benar Animal, kami membuatnya memeriksa lagi untuk memvalidasi para pemain.

Lebih efisien untuk melakukan ini sebagai gantinya:

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

The asoperator adalah pemain yang tidak akan melemparkan sebuah pengecualian jika gagal, bukan kembali null. Dengan cara ini, CLR memeriksa jenis objek hanya sekali, dan setelah itu, kita hanya perlu melakukan pemeriksaan nol, yang lebih efisien.

Tetapi waspadalah: banyak orang jatuh ke dalam perangkap as. Karena tidak membuang pengecualian, beberapa orang menganggapnya sebagai gips "aman", dan mereka menggunakannya secara eksklusif, menghindari gips biasa. Ini mengarah ke kesalahan seperti ini:

(o as Animal).Speak();

Dalam hal ini, pengembang dengan jelas mengasumsikan bahwa oakan selalu menjadi Animal, dan selama asumsinya benar, semuanya berfungsi dengan baik. Tetapi jika mereka salah, maka apa yang akhirnya mereka dapatkan di sini adalah a NullReferenceException. Dengan pemain reguler, mereka akan mendapatkan InvalidCastExceptiongantinya, yang akan lebih tepat mengidentifikasi masalah.

Terkadang, bug ini sulit ditemukan:

class Foo{
    readonly Animal animal;

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

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

Ini adalah kasus lain di mana pengembang jelas berharap omenjadi Animalsetiap waktu, tetapi ini tidak jelas dalam konstruktor, di mana para aspemain digunakan. Tidak jelas sampai Anda mencapai Interactmetode, di mana animalbidang diharapkan ditugaskan secara positif. Dalam hal ini, Anda tidak hanya berakhir dengan pengecualian yang menyesatkan, tetapi tidak dibuang hingga berpotensi lebih lama daripada saat kesalahan sebenarnya terjadi.

Singkatnya:

  • Jika Anda hanya perlu tahu apakah suatu objek memiliki tipe tertentu atau tidak, gunakan is.

  • Jika Anda perlu memperlakukan objek sebagai instance dari tipe tertentu, tetapi Anda tidak tahu pasti bahwa objek tersebut akan dari tipe itu, gunakan asdan periksa null.

  • Jika Anda perlu memperlakukan objek sebagai turunan dari jenis tertentu, dan objek tersebut seharusnya dari jenis itu, gunakan gips biasa.

P Ayah
sumber
apa yang salah dengan ini jika (o adalah Hewan) ((Hewan) o). Bicara (); ? dapatkah Anda memberikan detail lebih lanjut?
batmaci
2
@ Batmaci: ada di jawaban - itu menyebabkan dua jenis pemeriksaan. Pertama kali o is Animal, yang mengharuskan CLR untuk memeriksa apakah jenis variabelnya oadalah Animal. Kali kedua ia mengecek adalah ketika dilemparkan dalam pernyataan ((Animal)o).Speak(). Daripada memeriksa dua kali, periksa sekali menggunakan as.
siride
Saya menemukan ini penjelasan yang sangat bagus, terima kasih telah menjelaskan!
Paul Efford
16

Jika Anda menggunakan C # 7, maka sudah saatnya pembaruan untuk jawaban hebat Andrew Hare. Pencocokan pola telah memperkenalkan jalan pintas yang bagus yang memberi kita variabel yang diketik dalam konteks pernyataan if, tanpa memerlukan deklarasi / pemeran yang terpisah dan periksa:

if (obj1 is int integerValue)
{
    integerValue++;
}

Ini terlihat sangat underwhelming untuk pemain tunggal seperti ini, tetapi benar-benar bersinar ketika Anda memiliki banyak jenis yang mungkin masuk ke rutinitas Anda. Di bawah ini adalah cara lama untuk menghindari casting dua kali:

Button button = obj1 as Button;
if (button != null)
{
    // do stuff...
    return;
}
TextBox text = obj1 as TextBox;
if (text != null)
{
    // do stuff...
    return;
}
Label label = obj1 as Label;
if (label != null)
{
    // do stuff...
    return;
}
// ... and so on

Bekerja di sekitar menyusutkan kode ini sebanyak mungkin, serta menghindari gips duplikat dari objek yang sama selalu mengganggu saya. Di atas dikompresi dengan baik dengan pencocokan pola sebagai berikut:

switch (obj1)
{
    case Button button:
        // do stuff...
        break;
    case TextBox text:
        // do stuff...
        break;
    case Label label:
        // do stuff...
        break;
    // and so on...
}

EDIT: Memperbarui metode baru yang lebih lama untuk menggunakan sakelar sesuai komentar Palec.

JoelC
sumber
1
Dianjurkan untuk menggunakan switchpernyataan dengan pola yang cocok .
Palec
Bagaimana Anda mengatasinya? Dalam blok kode khusus ini? if (obj1 is int integerValue) { integerValue++; }
Ben Vertonghen
Ben, jika saya mengerti pertanyaan Anda, saya hanya akan memiliki pernyataan lain untuk menangani kasus-kasus lain karena Anda tidak dapat memasukkan integer ke dalam variabel integer. :)
JoelC
14

Saya memiliki Type-properti untuk dibandingkan dan tidak dapat menggunakan is(seperti my_type is _BaseTypetoLookFor), tapi saya bisa menggunakan ini:

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

Perhatikan itu IsInstanceOfTypedan IsAssignableFromkembali trueketika membandingkan jenis yang sama, di mana IsSubClassOf akan kembali false. Dan IsSubclassOftidak bekerja pada antarmuka, di mana dua lainnya. (Lihat juga pertanyaan dan jawaban ini .)

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
Yahoo Serius
sumber
9

Saya lebih suka itu

Yang mengatakan, jika Anda menggunakan itu , Anda kemungkinan tidak menggunakan warisan dengan benar.

Asumsikan Orang itu: Entitas, dan Hewan itu: Entitas. Umpan adalah metode virtual di Entity (untuk membuat Neil senang)

class Person
{
  // A Person should be able to Feed
  // another Entity, but they way he feeds
  // each is different
  public override void Feed( Entity e )
  {
    if( e is Person )
    {
      // feed me
    }
    else if( e is Animal )
    {
      // ruff
    }
  }
}

Agak

class Person
{
  public override void Feed( Person p )
  {
    // feed the person
  }
  public override void Feed( Animal a )
  {
    // feed the animal
  }
}
bobobobo
sumber
1
Benar, saya tidak akan pernah melakukan yang pertama, mengetahui bahwa Orang itu berasal dari Hewan.
jasonh
3
Yang terakhir ini tidak benar-benar menggunakan warisan. Foo harus menjadi metode virtual Entity yang diganti dalam Person and Animal.
Neil Williams
2
@obobobo Saya pikir maksud Anda "kelebihan beban", bukan "warisan".
lc.
@ lc: Tidak, maksudku warisan. Contoh pertama adalah semacam cara yang salah (menggunakan adalah ) untuk mendapatkan perilaku yang berbeda. Contoh kedua menggunakan ya overloading, tetapi menghindari penggunaan is .
bobobobo
1
Masalahnya dengan contoh adalah bahwa itu tidak akan skala. Jika Anda menambahkan entitas baru yang perlu makan (misalnya Serangga atau Monster), Anda perlu menambahkan metode baru di kelas Entity dan kemudian menimpanya dalam subclass yang akan memberinya makan. Ini tidak lebih disukai daripada daftar jika (entitas adalah X) lain jika (entitas adalah Y) ... Ini melanggar LSP dan OCP, warisan mungkin bukan solusi terbaik untuk masalah tersebut. Beberapa bentuk delegasi mungkin lebih disukai.
ebrown
5

Saya percaya yang terakhir juga melihat pewarisan (misalnya Dog is Animal == true), yang lebih baik dalam kebanyakan kasus.

StriplingWarrior
sumber
2

Tergantung pada apa yang saya lakukan. Jika saya membutuhkan nilai bool (katakanlah, untuk menentukan apakah saya akan menggunakan int), saya akan menggunakan is. Jika saya benar-benar membutuhkan jenis untuk beberapa alasan (katakanlah, untuk meneruskan ke metode lain) saya akan menggunakan GetType().

AllenG
sumber
1
Poin yang bagus. Saya lupa menyebutkan bahwa saya menjawab pertanyaan ini setelah melihat beberapa jawaban yang menggunakan pernyataan if untuk memeriksa suatu jenis.
jasonh
0

Yang terakhir lebih bersih, lebih jelas, dan juga memeriksa subtipe. Yang lain tidak memeriksa polimorfisme.

thecoop
sumber
0

Digunakan untuk mendapatkan objek System.Type untuk suatu jenis. Jenis ekspresi mengambil bentuk berikut:

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
    */

Sampel ini menggunakan metode GetType untuk menentukan tipe yang digunakan untuk memuat hasil perhitungan numerik. Ini tergantung pada persyaratan penyimpanan nomor yang dihasilkan.

    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
    */
Muhammad Awais
sumber
-4
if (c is UserControl) c.Enabled = enable;
Paulos02
sumber
4
Harap edit dengan informasi lebih lanjut. Jawaban khusus kode dan "coba ini" tidak disarankan, karena tidak mengandung konten yang dapat ditelusuri, dan jangan jelaskan mengapa seseorang harus "coba ini".
abarisone
Jawaban Anda tidak terkait dengan pertanyaan itu.
menxin
-5

Anda dapat menggunakan operator "typeof ()" di C # tetapi Anda harus memanggil namespace menggunakan System.IO; Anda harus menggunakan kata kunci "is" jika Anda ingin memeriksa suatu jenis.

androidrill
sumber
7
typeoftidak didefinisikan dalam namespace, itu adalah kata kunci. System.IOtidak ada hubungannya dengan ini.
Arturo Torres Sánchez
-5

Jenis tes kinerja () 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();
    }
}

Hasil dalam mode debug:

00:00:08.4096636
00:00:10.8570657

Hasil dalam mode rilis:

00:00:02.3799048
00:00:07.1797128
Alexander Vasilyev
sumber
1
Seseorang seharusnya tidak menggunakan DateTime. Tidak Aktif Sekarang untuk pengukuran kinerja. Dengan kode Anda tetapi dengan kelas Stopwatch saya mendapatkan hasil yang berlawanan secara terus-menerus untuk mode Debug. UseTypeOf: 00: 00: 14.5074469 UseGetType: 00: 00: 10.5799534. Mode rilisnya sama, seperti yang diharapkan
Alexey Shcherbak
@AlexeyShcherbak Perbedaan antara Stopwatch dan DateTime. Sekarang tidak boleh lebih dari 10-20 ms, periksa kode Anda lagi. Dan saya tidak peduli dengan milidetik dalam pengujian saya. Kode saya juga akan lebih panjang beberapa baris kode dengan Stopwatch.
Alexander Vasilyev
1
itu praktik buruk secara umum, bukan dalam kasus khusus Anda.
Alexey Shcherbak
4
@AlexanderVasilyev Jumlah baris kode tidak boleh digunakan sebagai argumen untuk melakukan sesuatu yang secara dokumenter menyesatkan. Seperti yang terlihat di msdn.microsoft.com/en-us/library/system.datetime(v=vs.110).aspx , DateTimetidak boleh digunakan jika Anda khawatir tentang waktu di bawah 100ms , karena menggunakan jangka waktu OS. Dibandingkan dengan Stopwatch, yang menggunakan prosesor Tick, resolusi yang digunakan oleh DateTimedalam Win7 adalah 15ms rejan.
Eric Wu