Câu hỏi Loại kiểm tra: typeof, GetType, hoặc là?


Tôi đã thấy nhiều người sử dụng mã sau:

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

Nhưng tôi biết bạn cũng có thể làm điều này:

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

Hoặc điều này:

if (obj1 is int)
    // Some code here

Cá nhân, tôi cảm thấy người cuối cùng là người sạch nhất, nhưng có điều gì đó tôi đang thiếu? Cái nào là tốt nhất để sử dụng, hoặc nó là sở thích cá nhân?


1187
2018-06-11 19:10


gốc


Đừng quên as! - RCIX
as không thực sự kiểm tra kiểu ... - jasonh
as chắc chắn là một dạng kiểm tra kiểu, mỗi bit nhiều như is Là! Nó sử dụng hiệu quả is đằng sau hậu trường, và được sử dụng khắp nơi trong MSDN ở những nơi mà nó cải thiện độ sạch mã so với is. Thay vì kiểm tra is đầu tiên, một cuộc gọi đến as thiết lập một biến đã gõ sẵn sàng để sử dụng: Nếu nó là null, trả lời thích hợp; nếu không, hãy tiếp tục. Chắc chắn một cái gì đó tôi đã nhìn thấy và sử dụng khá một chút. - Zaccone
Có sự khác biệt đáng kể về hiệu suất as/is (được bao gồm trong stackoverflow.com/a/27813381/477420) giả định các tác phẩm ngữ nghĩa của nó cho trường hợp của bạn. - Alexei Levenkov
@samusarin nó không "sử dụng" sự phản chiếu. Các GetType phương pháp bạn đang liên kết đến System.Reflection.Assembly - một phương pháp hoàn toàn khác và không liên quan ở đây. - Kirk Woll


Các câu trả lời:


Tất cả đều khác nhau.

  • typeof có một tên kiểu (mà bạn chỉ định tại thời gian biên dịch).
  • GetType lấy kiểu thời gian chạy của một cá thể.
  • is trả về true nếu một thể hiện nằm trong cây thừa kế.

Thí dụ

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

Thế còn typeof(T)? Nó cũng được giải quyết tại thời gian biên dịch?

Vâng. T luôn là loại biểu thức. Hãy nhớ rằng, một phương pháp chung về cơ bản là một bó toàn bộ các phương thức với kiểu thích hợp. Thí dụ:

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"

1497
2018-06-11 19:15



Ah, vì vậy nếu tôi có một lớp Ford có nguồn gốc từ Ô tô và một ví dụ của Ford, kiểm tra "là Xe hơi" trên trường hợp đó sẽ là sự thật. Có ý nghĩa! - jasonh
Để làm rõ, tôi đã nhận thức được điều đó, nhưng tôi đã nhận xét trước khi bạn thêm một mẫu mã. Tôi muốn cố gắng thêm một số rõ ràng bằng tiếng Anh cho câu trả lời đã xuất sắc của bạn. - jasonh
@Jimmy Vì vậy, những gì về hiệu suất? typeof so với GetType()? - Shimmy
@Shimmy nếu typeof được đánh giá tại thời gian biên dịch và GetType () được đánh giá trong thời gian chạy, sau đó nó có ý nghĩa rằng GetType () gánh chịu một hiệu suất nhỏ hit - Cedric Mamo
@PrerakK new Dog().GetType() is Animal trả về false (và phiên bản khác của bạn) kể từ khi .GetType() trả về một đối tượng kiểu Typevà Type không phải là Animal. - Maarten


Sử dụng typeof khi bạn muốn lấy loại thời gian biên soạn. Sử dụng GetType khi bạn muốn lấy loại thời gian thực hiện. Có rất ít trường hợp sử dụng is vì nó có một dàn diễn viên và, trong hầu hết các trường hợp, bạn vẫn sẽ đúc biến đó.

Có một tùy chọn thứ tư mà bạn chưa xem xét (đặc biệt là nếu bạn định truyền một đối tượng cho kiểu bạn tìm thấy); đó là sử dụng as.

Foo foo = obj as Foo;

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

Chỉ sử dụng một cast trong khi phương pháp này:

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

đòi hỏi hai.


163
2018-06-11 19:14



Với những thay đổi trong .NET 4 is vẫn thực hiện một dàn diễn viên? - ahsteele
Câu trả lời này có đúng không? Có đúng là bạn thực sự có thể vượt qua một thể hiện vào typeof ()? Kinh nghiệm của tôi là không. Nhưng tôi đoán nó đúng là việc kiểm tra một thể hiện có thể xảy ra trong thời gian chạy, trong khi việc kiểm tra một lớp sẽ có thể thực hiện được tại thời gian biên dịch. - Jon Coombs
@jon (4 tuổi sau q của bạn.), không, bạn không thể vượt qua một ví dụ typeof()và câu trả lời này không gợi ý bạn có thể. Thay vào đó, bạn chuyển vào loại, tức là typeof(string) công trinh, typeof("foo") không làm. - Abel


1.

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

Điều này là bất hợp pháp, bởi vì typeof chỉ hoạt động trên các loại, chứ không phải trên các biến. Tôi giả sử obj1 là một biến. Vì vậy, bằng cách này typeof là tĩnh, và làm công việc của mình tại thời gian biên dịch thay vì thời gian chạy.

2.

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

Điều này đúng nếu obj1 chính xác là kiểu int. Nếu obj1 xuất phát từ int, điều kiện if sẽ là false.

3.

if (obj1 is int)

Điều này đúng nếu obj1 là một int, hoặc nếu nó xuất phát từ một lớp được gọi là int, hoặc nếu nó thực hiện một giao diện được gọi là int.


59
2018-06-11 19:17



Suy nghĩ về 1, bạn nói đúng. Tuy nhiên, tôi đã nhìn thấy nó trong một số mẫu mã ở đây. Nó phải là Type t = obj1.GetType (); - jasonh
Đúng, tôi nghĩ vậy. "typeof (obj1)" không biên dịch khi tôi thử. - Scott Langham
Không thể lấy được từ System.Int32 hoặc bất kỳ loại giá trị nào khác trong C # - reggaeguitar
bạn có thể nói điều gì sẽ là typeof (typeof (system.int32)) - Sana
@ Sana, tại sao bạn không thử nó :) Tôi sẽ tưởng tượng mặc dù bạn lấy lại một thể hiện của System.Type đại diện cho loại System.Type! Tài liệu cho typeof là ở đây: docs.microsoft.com/en-us/dotnet/csharp/language-reference/… - Scott Langham


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

Đây là lỗi. Toán tử typeof trong C # chỉ có thể lấy tên kiểu, không phải các đối tượng.

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

Điều này sẽ làm việc, nhưng có thể không như bạn mong đợi. Đối với các loại giá trị, như bạn đã hiển thị ở đây, nó có thể chấp nhận được, nhưng đối với các loại tham chiếu, nó sẽ chỉ trả về true nếu kiểu đó là hoàn toàn giống nhau loại, không phải cái gì khác trong hệ thống phân cấp thừa kế. Ví dụ:

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

Điều này sẽ in "o is something else", bởi vì loại o Là Dog, không phải Animal. Bạn có thể thực hiện công việc này, tuy nhiên, nếu bạn sử dụng IsAssignableFrom phương pháp của Type lớp học.

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

Tuy nhiên, kỹ thuật này vẫn để lại một vấn đề lớn. Nếu biến của bạn là null, lệnh gọi tới GetType() sẽ ném một NullReferenceException. Vì vậy, để làm cho nó hoạt động chính xác, bạn sẽ làm:

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

Với điều này, bạn có hành vi tương đương của is từ khóa. Do đó, nếu đây là hành vi bạn muốn, bạn nên sử dụng is từ khóa, dễ đọc hơn và hiệu quả hơn.

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

Tuy nhiên, trong hầu hết các trường hợp, is từ khóa vẫn không phải là những gì bạn thực sự muốn, bởi vì nó thường không đủ để biết rằng một đối tượng là một loại nhất định. Thông thường, bạn muốn thực sự sử dụng đối tượng đó như là một thể hiện của kiểu đó, nó cũng đòi hỏi phải đúc nó. Và vì vậy bạn có thể thấy mình viết mã như thế này:

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

Nhưng điều đó làm cho CLR kiểm tra loại đối tượng lên đến hai lần. Nó sẽ kiểm tra nó một lần để đáp ứng is toán tử và nếu o thực sự là một Animal, chúng tôi sẽ kiểm tra lại để xác thực diễn viên.

Thay vào đó, thực hiện điều này hiệu quả hơn:

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

Các as toán tử là một diễn viên sẽ không ném một ngoại lệ nếu nó không thành công, thay vào đó trở lại null. Bằng cách này, CLR kiểm tra kiểu của đối tượng chỉ một lần, và sau đó, chúng ta chỉ cần thực hiện kiểm tra null, điều này hiệu quả hơn.

Nhưng hãy cẩn thận: nhiều người rơi vào một cái bẫy với as. Bởi vì nó không ném ngoại lệ, một số người nghĩ rằng nó là một diễn viên "an toàn", và họ sử dụng nó độc quyền, các cảnh quay thường xuyên. Điều này dẫn đến các lỗi như sau:

(o as Animal).Speak();

Trong trường hợp này, nhà phát triển rõ ràng là giả định rằng o sẽ luôn luôn hạt đậu Animal, và miễn là giả định của họ là chính xác, mọi thứ đều hoạt động tốt. Nhưng nếu họ sai, thì những gì họ kết thúc ở đây là NullReferenceException. Với dàn diễn viên thường xuyên, họ sẽ nhận được InvalidCastException thay vào đó, điều này sẽ xác định chính xác hơn vấn đề.

Đôi khi, lỗi này có thể khó tìm:

class Foo{
    readonly Animal animal;

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

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

Đây là trường hợp khác mà nhà phát triển đang mong đợi rõ ràng o là một Animal mọi lúc, nhưng điều này không rõ ràng trong nhà xây dựng, nơi as cast được sử dụng. Nó không hiển nhiên cho đến khi bạn đến Interact phương pháp, nơi animal trường được dự kiến ​​sẽ được gán một cách tích cực. Trong trường hợp này, không chỉ bạn kết thúc với một ngoại lệ gây hiểu lầm, nhưng nó không được ném cho đến khi có khả năng muộn hơn nhiều so với khi xảy ra lỗi thực tế.

Tóm tắt:

  • Nếu bạn chỉ cần biết một đối tượng có thuộc loại nào hay không, hãy sử dụng is.

  • Nếu bạn cần xử lý một đối tượng như một thể hiện của một kiểu nhất định, nhưng bạn không biết chắc chắn rằng đối tượng sẽ thuộc loại đó, hãy sử dụng as và kiểm tra null.

  • Nếu bạn cần xử lý một đối tượng như một thể hiện của một kiểu nhất định nào đó, và đối tượng được cho là thuộc loại đó, hãy sử dụng một diễn viên bình thường.


39
2018-06-11 19:34



những gì là sai với điều này nếu (o là động vật) ((động vật) o) .Speak (); ? bạn có thể cho biết thêm chi tiết không? - batmaci
@batmaci: đó là trong câu trả lời - nó gây ra hai loại kiểm tra. Lần đầu tiên là o is Animal, yêu cầu CLR kiểm tra xem loại biến o là một Animal. Lần thứ hai nó kiểm tra là khi nó diễn ra trong câu lệnh ((Animal)o).Speak(). Thay vì kiểm tra hai lần, hãy kiểm tra một lần bằng as. - siride


Tôi đã có một Type-property để so sánh và không thể sử dụng is (như my_type is _BaseTypetoLookFor), nhưng tôi có thể sử dụng chúng:

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

Thông báo rằng IsInstanceOfType và IsAssignableFrom trở về true khi so sánh cùng loại, trong đó IsSubClassOf sẽ trả về false. Và IsSubclassOf không hoạt động trên các giao diện, nơi hai người kia làm. (Xem thêm câu hỏi và câu trả lời này.)

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

11
2018-05-15 10:39





tôi thích

Điều đó nói rằng, nếu bạn đang sử dụng bạn có thể không phải sử dụng thừa kế đúng cách.

Giả sử rằng Person: Entity và Animal: Entity. Feed là một phương thức ảo trong Entity (để làm cho Neil hạnh phúc)

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

Hơn

class Person
{
  public override void Feed( Person p )
  {
    // feed the person
  }
  public override void Feed( Animal a )
  {
    // feed the animal
  }
}

7
2018-06-11 19:15



Đúng vậy, tôi sẽ không bao giờ làm cái cũ, biết rằng Con người xuất phát từ Động vật. - jasonh
Cái sau không thực sự sử dụng thừa kế. Foo phải là một phương thức ảo của thực thể được ghi đè trong Person and Animal. - Neil Williams
@bobobobo Tôi nghĩ rằng bạn có nghĩa là "quá tải", không phải "thừa kế". - lc.
@ lc: Không, ý tôi là thừa kế. Ví dụ đầu tiên là một cách không chính xác (sử dụng Là) để có được hành vi khác nhau. Ví dụ thứ hai sử dụng quá tải có, nhưng tránh sử dụng Là. - bobobobo
Vấn đề với ví dụ là nó sẽ không mở rộng quy mô. Nếu bạn đã thêm các thực thể mới cần ăn (ví dụ như một Côn trùng hoặc Quái vật), bạn sẽ cần phải thêm một phương thức mới trong lớp Entity và sau đó ghi đè lên nó trong các lớp con sẽ nạp nó. Đây không phải là bất kỳ thích hợp hơn một danh sách nếu (thực thể là X) khác nếu (thực thể là Y) ... Điều này vi phạm LSP và OCP, kế thừa có lẽ không phải là giải pháp tốt nhất cho vấn đề. Một số hình thức phái đoàn có lẽ sẽ được ưa thích. - ebrown


Nếu bạn đang sử dụng C # 7, thì đã đến lúc cập nhật câu trả lời tuyệt vời của Andrew Hare. Khớp mẫu đã giới thiệu một phím tắt đẹp cho chúng ta một biến được gõ trong ngữ cảnh của câu lệnh if, mà không cần khai báo / đúc và kiểm tra riêng biệt:

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

Điều này trông khá underwhelming cho một diễn viên như thế này, nhưng thực sự tỏa sáng khi bạn có nhiều loại có thể đi vào thói quen của bạn. Dưới đây là cách cũ để tránh truyền hai lần:

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

Làm việc xung quanh thu hẹp mã này càng nhiều càng tốt, cũng như tránh trùng lặp phôi của cùng một đối tượng đã luôn luôn làm phiền tôi. Ở trên là độc đáo nén với mô hình phù hợp với những điều sau đây:

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

EDIT: Cập nhật các phương pháp mới hơn để sử dụng một chuyển đổi theo bình luận của Palec.


6
2018-02-01 15:47



Sử dụng switch tuyên bố với đối sánh mẫu được khuyến khích trong trường hợp này. - Palec


Tôi tin rằng người cuối cùng cũng nhìn vào thừa kế (ví dụ: Chó là Động vật == đúng), điều tốt hơn trong hầu hết các trường hợp.


5
2018-06-11 19:14





Nó phụ thuộc vào những gì tôi đang làm. Nếu tôi cần một giá trị bool (nói, để xác định xem tôi sẽ cast vào một int), tôi sẽ sử dụng is. Nếu tôi thực sự cần loại cho một số lý do (nói, để vượt qua một số phương pháp khác) tôi sẽ sử dụng GetType().


2
2018-06-11 19:14



Điểm tốt. Tôi quên đề cập đến rằng tôi đã nhận câu hỏi này sau khi xem xét một số câu trả lời đã sử dụng câu lệnh if để kiểm tra một loại. - jasonh


Người cuối cùng là sạch hơn, rõ ràng hơn và cũng kiểm tra các loại phụ. Những người khác không kiểm tra tính đa hình.


0
2018-06-11 19:16