Hiệu suất toán tử C # 'là'


102

Tôi có một chương trình yêu cầu hiệu suất nhanh. Trong một trong các vòng lặp bên trong của nó, tôi cần kiểm tra kiểu của một đối tượng để xem liệu nó có kế thừa từ một giao diện nhất định hay không.

Một cách để làm điều này là sử dụng chức năng kiểm tra kiểu tích hợp của CLR. Phương pháp thanh lịch nhất có lẽ là từ khóa 'là':

if (obj is ISpecialType)

Một cách tiếp cận khác là cung cấp cho lớp cơ sở hàm GetType () ảo của riêng tôi, hàm này trả về một giá trị enum được xác định trước (trong trường hợp của tôi, thực sự, tôi chỉ cần bool). Phương pháp đó sẽ nhanh chóng, nhưng kém thanh lịch hơn.

Tôi đã nghe nói rằng có một hướng dẫn IL dành riêng cho từ khóa 'is', nhưng điều đó không có nghĩa là nó thực thi nhanh khi được dịch sang hợp ngữ gốc. Bất cứ ai có thể chia sẻ một số thông tin chi tiết về hiệu suất của 'is' so với phương pháp khác?

CẬP NHẬT: Cảm ơn vì tất cả các câu trả lời đầy đủ thông tin! Có vẻ như một số điểm hữu ích được lan truyền trong số các câu trả lời: Quan điểm của Andrew về việc 'tự động thực hiện một diễn viên là điều cần thiết, nhưng dữ liệu hiệu suất được thu thập bởi Binary Worrier và Ian cũng cực kỳ hữu ích. Sẽ thật tuyệt nếu một trong những câu trả lời được chỉnh sửa để bao gồm tất cả thông tin này.


2
btw, CLR sẽ không cung cấp cho bạn một khả năng để tạo ra chức năng riêng Loại GetType () của bạn, bởi vì nó phá vỡ một trong những quy tắc CLR chính - thực sự loại
abatishchev

1
Ồ, tôi không hoàn toàn chắc chắn ý của bạn về quy tắc "thực sự loại", nhưng tôi hiểu rằng CLR có một hàm Type GetType () được tích hợp sẵn. Nếu tôi sử dụng phương thức đó, nó sẽ có một hàm có tên khác trả về một số enum, vì vậy sẽ không có bất kỳ xung đột tên / ký hiệu nào.
JubJub

3
Tôi nghĩ abatishchev có nghĩa là "an toàn kiểu". GetType () là không ảo để ngăn một kiểu nói dối về chính nó và do đó bảo vệ an toàn cho kiểu.
Andrew Hare

2
Bạn đã xem xét việc tìm nạp trước và lưu vào bộ nhớ đệm có tuân thủ kiểu để bạn không phải thực hiện việc đó trong vòng lặp không? Có vẻ như mọi câu hỏi về hoàn hảo luôn được +1 ồ ạt nhưng điều này có vẻ như là sự kém hiểu biết về c # đối với tôi. Nó thực sự quá chậm? Làm sao? Bạn đã thử những gì? Rõ ràng là không nhiều cho bình luận của bạn về câu trả lời ...
Gusdor

Câu trả lời:


114

Việc sử dụng iscó thể ảnh hưởng đến hiệu suất nếu sau khi bạn kiểm tra loại, bạn chuyển sang loại đó. isthực sự chuyển đối tượng sang kiểu bạn đang kiểm tra, vì vậy mọi quá trình truyền tiếp theo là dư thừa.

Nếu bạn vẫn định truyền, đây là một cách tiếp cận tốt hơn:

ISpecialType t = obj as ISpecialType;

if (t != null)
{
    // use t here
}

1
Cảm ơn. Nhưng nếu tôi sẽ không ép kiểu đối tượng nếu điều kiện không thành công, tôi có nên sử dụng một hàm ảo để kiểm tra kiểu thay thế không?
JubJub

4
@JubJub: không. Một lỗi asvề cơ bản thực hiện cùng một hoạt động như is(cụ thể là kiểm tra kiểu). Sự khác biệt duy nhất là nó sau đó trả về nullthay vì false.
Konrad Rudolph

74

Tôi với Ian , chắc bạn không muốn làm điều này.

Tuy nhiên, bạn biết đấy, có rất ít sự khác biệt giữa hai điều này, hơn 10.000.000 lần lặp

  • Kiểm tra enum có tốc độ 700 mili giây (ước chừng)
  • Kiểm tra IS xuất hiện ở 1000 mili giây (ước chừng)

Cá nhân tôi sẽ không khắc phục sự cố này theo cách này, nhưng nếu tôi buộc phải chọn một phương pháp thì đó sẽ là kiểm tra IS tích hợp sẵn, sự khác biệt về hiệu suất không đáng để xem xét chi phí mã hóa.

Các lớp cơ sở và dẫn xuất của tôi

class MyBaseClass
{
    public enum ClassTypeEnum { A, B }
    public ClassTypeEnum ClassType { get; protected set; }
}

class MyClassA : MyBaseClass
{
    public MyClassA()
    {
        ClassType = MyBaseClass.ClassTypeEnum.A;
    }
}
class MyClassB : MyBaseClass
{
    public MyClassB()
    {
        ClassType = MyBaseClass.ClassTypeEnum.B;
    }
}

JubJub: Theo yêu cầu thêm thông tin về các bài kiểm tra.

Tôi đã chạy cả hai bài kiểm tra từ một ứng dụng bảng điều khiển (một bản dựng gỡ lỗi), mỗi bài kiểm tra trông giống như sau

static void IsTest()
{
    DateTime start = DateTime.Now;
    for (int i = 0; i < 10000000; i++)
    {
        MyBaseClass a;
        if (i % 2 == 0)
            a = new MyClassA();
        else
            a = new MyClassB();
        bool b = a is MyClassB;
    }
    DateTime end = DateTime.Now;
    Console.WriteLine("Is test {0} miliseconds", (end - start).TotalMilliseconds);
}

Chạy trong bản phát hành, tôi nhận được sự khác biệt từ 60-70 ms, giống như Ian.

Cập nhật thêm - Ngày 25 tháng 10 năm 2012
Sau một vài năm trôi qua, tôi nhận thấy điều gì đó về điều này, trình biên dịch có thể chọn bỏ qua bool b = a is MyClassBtrong bản phát hành vì b không được sử dụng ở bất kỳ đâu.

Mã này. . .

public static void IsTest()
{
    long total = 0;
    var a = new MyClassA();
    var b = new MyClassB();
    var sw = new Stopwatch();
    sw.Start();
    for (int i = 0; i < 10000000; i++)
    {
        MyBaseClass baseRef;
        if (i % 2 == 0)
            baseRef = a;//new MyClassA();
        else
            baseRef = b;// new MyClassB();
        //bool bo = baseRef is MyClassB;
        bool bo = baseRef.ClassType == MyBaseClass.ClassTypeEnum.B;
        if (bo) total += 1;
    }
    sw.Stop();
    Console.WriteLine("Is test {0} miliseconds {1}", sw.ElapsedMilliseconds, total);
}

. . . liên tục hiển thị iskiểm tra đến ở khoảng 57 mili giây và so sánh enum đến ở 29 mili giây.

NB Tôi vẫn thích isséc hơn, sự khác biệt quá nhỏ để quan tâm đến


35
+1 để thực sự kiểm tra hiệu suất, thay vì giả định.
Jon Tackabury

3
Sẽ tốt hơn nhiều việc phải làm thử nghiệm với lớp Stopwatch, thay vì DateTime.Now đó là rất tốn kém
abatishchev

2
Tôi sẽ đưa nó lên tàu, tuy nhiên trong trường hợp này, tôi không nghĩ nó sẽ ảnh hưởng đến kết quả. Cảm ơn :)
Binary Worrier

11
@Binary Worrier- Việc phân bổ toán tử mới của bạn cho các lớp sẽ hoàn toàn làm lu mờ bất kỳ sự khác biệt nào về hiệu suất trong các phép toán 'is'. Tại sao bạn không loại bỏ các thao tác mới đó , bằng cách sử dụng lại hai trường hợp được phân bổ trước khác nhau, sau đó chạy lại mã và đăng kết quả của bạn.

1
@mcmillab: Tôi sẽ đảm bảo rằng bất cứ điều gì bạn đang làm, bạn sẽ gặp phải nhiều nút thắt lớn hơn bất kỳ sự suy giảm hiệu suất nào mà isnhà điều hành đang gây ra cho bạn và rằng việc nghe nói nhiều về thiết kế & mã hóa xung quanh isnhà điều hành sẽ khiến bạn phải trả giá chất lượng mã và cuối cùng cũng sẽ là hiệu suất tự đánh bại. Trong trường hợp này, tôi giữ nguyên tuyên bố của mình. Các 'là' hành là bao giờ sẽ là các vấn đề với hiệu suất thời gian chạy của bạn.
Binary Worrier

23

Ok vì vậy tôi đã trò chuyện về điều này với một người nào đó và quyết định thử nghiệm điều này nhiều hơn. Theo như tôi có thể nói, hiệu suất của asiscả hai đều rất tốt, so với việc kiểm tra thành viên hoặc chức năng lưu trữ thông tin loại của chính bạn.

Tôi đã sử dụng Stopwatch, mà tôi mới học có thể không phải là cách tiếp cận đáng tin cậy nhất, vì vậy tôi cũng đã thử UtcNow. Sau đó, tôi cũng đã thử cách tiếp cận thời gian của Bộ xử lý có vẻ tương tự như UtcNowbao gồm thời gian tạo không thể đoán trước. Tôi cũng đã thử làm cho lớp cơ sở không trừu tượng và không có ảo nhưng nó dường như không có tác dụng đáng kể.

Tôi đã chạy điều này trên Quad Q6600 với RAM 16GB. Ngay cả với 50 phút lặp lại, các con số vẫn trả về khoảng +/- 50 hoặc hơn mili giây nên tôi sẽ không đọc quá nhiều về những khác biệt nhỏ.

Thật thú vị khi thấy x64 được tạo nhanh hơn nhưng được thực thi như / chậm hơn x86

x64 Chế độ phát hành:
Đồng hồ bấm giờ:
Như: 561ms
Là: 597ms
Thuộc tính cơ sở: 539ms Trường
cơ sở: 555ms Trường
RO cơ sở: 552ms
Thử nghiệm
GetEnumType () ảo : 556ms Thử nghiệm IsB ảo (): 588ms
Thời gian tạo: 10416ms

UtcNow:
As: 499ms
Is: 532ms
Thuộc tính cơ sở: 479ms Trường
cơ sở: 502ms Trường
RO cơ sở: 491ms
Virtual GetEnumType (): 502ms
Virtual bool IsB (): 522ms
Thời gian tạo: 285ms (Con số này có vẻ không đáng tin cậy với UtcNow. Tôi cũng nhận được 109ms và 806ms.)

x86 Chế độ phát hành:
Đồng hồ bấm giờ:
Như: 391ms
Là: 423ms
Thuộc tính cơ sở: 369ms Trường
cơ sở: 321ms Trường
RO cơ sở: 339ms
Thử nghiệm
GetEnumType () ảo : 361ms Thử nghiệm IsB ảo (): 365ms
Thời gian tạo: 14106ms

UtcNow:
As: 348ms
Là: 375ms
Thuộc tính cơ sở: 329ms Trường
cơ sở: 286ms Trường
RO cơ sở: 309ms
Virtual GetEnumType (): 321ms
Virtual bool IsB (): 332ms
Thời gian tạo: 544ms (Con số này có vẻ không đáng tin cậy với UtcNow.)

Đây là hầu hết các mã:

    static readonly int iterations = 50000000;
    void IsTest()
    {
        Process.GetCurrentProcess().ProcessorAffinity = (IntPtr)1;
        MyBaseClass[] bases = new MyBaseClass[iterations];
        bool[] results1 = new bool[iterations];

        Stopwatch createTime = new Stopwatch();
        createTime.Start();
        DateTime createStart = DateTime.UtcNow;
        for (int i = 0; i < iterations; i++)
        {
            if (i % 2 == 0) bases[i] = new MyClassA();
            else bases[i] = new MyClassB();
        }
        DateTime createStop = DateTime.UtcNow;
        createTime.Stop();


        Stopwatch isTimer = new Stopwatch();
        isTimer.Start();
        DateTime isStart = DateTime.UtcNow;
        for (int i = 0; i < iterations; i++)
        {
            results1[i] =  bases[i] is MyClassB;
        }
        DateTime isStop = DateTime.UtcNow; 
        isTimer.Stop();
        CheckResults(ref  results1);

        Stopwatch asTimer = new Stopwatch();
        asTimer.Start();
        DateTime asStart = DateTime.UtcNow;
        for (int i = 0; i < iterations; i++)
        {
            results1[i] = bases[i] as MyClassB != null;
        }
        DateTime asStop = DateTime.UtcNow; 
        asTimer.Stop();
        CheckResults(ref  results1);

        Stopwatch baseMemberTime = new Stopwatch();
        baseMemberTime.Start();
        DateTime baseStart = DateTime.UtcNow;
        for (int i = 0; i < iterations; i++)
        {
            results1[i] = bases[i].ClassType == MyBaseClass.ClassTypeEnum.B;
        }
        DateTime baseStop = DateTime.UtcNow;
        baseMemberTime.Stop();
        CheckResults(ref  results1);

        Stopwatch baseFieldTime = new Stopwatch();
        baseFieldTime.Start();
        DateTime baseFieldStart = DateTime.UtcNow;
        for (int i = 0; i < iterations; i++)
        {
            results1[i] = bases[i].ClassTypeField == MyBaseClass.ClassTypeEnum.B;
        }
        DateTime baseFieldStop = DateTime.UtcNow;
        baseFieldTime.Stop();
        CheckResults(ref  results1);


        Stopwatch baseROFieldTime = new Stopwatch();
        baseROFieldTime.Start();
        DateTime baseROFieldStart = DateTime.UtcNow;
        for (int i = 0; i < iterations; i++)
        {
            results1[i] = bases[i].ClassTypeField == MyBaseClass.ClassTypeEnum.B;
        }
        DateTime baseROFieldStop = DateTime.UtcNow;
        baseROFieldTime.Stop();
        CheckResults(ref  results1);

        Stopwatch virtMethTime = new Stopwatch();
        virtMethTime.Start();
        DateTime virtStart = DateTime.UtcNow;
        for (int i = 0; i < iterations; i++)
        {
            results1[i] = bases[i].GetClassType() == MyBaseClass.ClassTypeEnum.B;
        }
        DateTime virtStop = DateTime.UtcNow;
        virtMethTime.Stop();
        CheckResults(ref  results1);

        Stopwatch virtMethBoolTime = new Stopwatch();
        virtMethBoolTime.Start();
        DateTime virtBoolStart = DateTime.UtcNow;
        for (int i = 0; i < iterations; i++)
        {
            results1[i] = bases[i].IsB();
        }
        DateTime virtBoolStop = DateTime.UtcNow;
        virtMethBoolTime.Stop();
        CheckResults(ref  results1);


        asdf.Text +=
        "Stopwatch: " + Environment.NewLine 
          +   "As:  " + asTimer.ElapsedMilliseconds + "ms" + Environment.NewLine
           +"Is:  " + isTimer.ElapsedMilliseconds + "ms" + Environment.NewLine
           + "Base property:  " + baseMemberTime.ElapsedMilliseconds + "ms" + Environment.NewLine + "Base field:  " + baseFieldTime.ElapsedMilliseconds + "ms" + Environment.NewLine + "Base RO field:  " + baseROFieldTime.ElapsedMilliseconds + "ms" + Environment.NewLine + "Virtual GetEnumType() test:  " + virtMethTime.ElapsedMilliseconds + "ms" + Environment.NewLine + "Virtual IsB() test:  " + virtMethBoolTime.ElapsedMilliseconds + "ms" + Environment.NewLine + "Create Time :  " + createTime.ElapsedMilliseconds + "ms" + Environment.NewLine + Environment.NewLine+"UtcNow: " + Environment.NewLine + "As:  " + (asStop - asStart).Milliseconds + "ms" + Environment.NewLine + "Is:  " + (isStop - isStart).Milliseconds + "ms" + Environment.NewLine + "Base property:  " + (baseStop - baseStart).Milliseconds + "ms" + Environment.NewLine + "Base field:  " + (baseFieldStop - baseFieldStart).Milliseconds + "ms" + Environment.NewLine + "Base RO field:  " + (baseROFieldStop - baseROFieldStart).Milliseconds + "ms" + Environment.NewLine + "Virtual GetEnumType():  " + (virtStop - virtStart).Milliseconds + "ms" + Environment.NewLine + "Virtual bool IsB():  " + (virtBoolStop - virtBoolStart).Milliseconds + "ms" + Environment.NewLine + "Create Time :  " + (createStop-createStart).Milliseconds + "ms" + Environment.NewLine;
    }
}

abstract class MyBaseClass
{
    public enum ClassTypeEnum { A, B }
    public ClassTypeEnum ClassType { get; protected set; }
    public ClassTypeEnum ClassTypeField;
    public readonly ClassTypeEnum ClassTypeReadonlyField;
    public abstract ClassTypeEnum GetClassType();
    public abstract bool IsB();
    protected MyBaseClass(ClassTypeEnum kind)
    {
        ClassTypeReadonlyField = kind;
    }
}

class MyClassA : MyBaseClass
{
    public override bool IsB() { return false; }
    public override ClassTypeEnum GetClassType() { return ClassTypeEnum.A; }
    public MyClassA() : base(MyBaseClass.ClassTypeEnum.A)
    {
        ClassType = MyBaseClass.ClassTypeEnum.A;
        ClassTypeField = MyBaseClass.ClassTypeEnum.A;            
    }
}
class MyClassB : MyBaseClass
{
    public override bool IsB() { return true; }
    public override ClassTypeEnum GetClassType() { return ClassTypeEnum.B; }
    public MyClassB() : base(MyBaseClass.ClassTypeEnum.B)
    {
        ClassType = MyBaseClass.ClassTypeEnum.B;
        ClassTypeField = MyBaseClass.ClassTypeEnum.B;
    }
}

45
(Một số phần thưởng 5 am-inspired Shakespeare ...) Có hay không: đó là câu hỏi: Liệu 'người trong mã có phải chịu đựng các phép liệt kê và thuộc tính của các cơ sở trừu tượng hay không, Hay để nhận lời đề nghị của một bên trung gian nhà ngôn ngữ học Và bằng cách viện dẫn chỉ dẫn của nó, hãy tin tưởng họ? Để đoán: để tự hỏi; Không còn nữa; và đến một thời điểm để phân biệt, chúng ta sẽ chấm dứt cơn đau đầu và hàng nghìn tiềm thức tự hỏi rằng những người lập trình có thời hạn là người thừa kế. 'Đó là một sự đóng cửa Thành tâm mong ước. Để chết, không, nhưng để ngủ; Vâng, tôi sẽ ngủ, cơ hội để mơ là và như trong những gì có thể bắt nguồn từ cơ sở nhất của lớp.
Jared Thirsk

Từ đó chúng ta có thể kết luận rằng truy cập một thuộc tính nhanh hơn trên x64 sau đó truy cập một trường !!! Bởi vì đó là một địa ngục của một bất ngờ đối với tôi làm thế nào điều này có thể được?
Didier A.

1
Tôi sẽ không kết luận điều đó, bởi vì: "Ngay cả với 50 triệu lần lặp, các con số vẫn bị trả lại khoảng +/- 50 hoặc hơn mili giây nên tôi sẽ không đọc quá nhiều về những khác biệt nhỏ."
Jared Thirsk

16

Andrew đúng. Trên thực tế, với phân tích mã, điều này được Visual Studio báo cáo là một quá trình truyền không cần thiết.

Một ý tưởng (mà không cần biết bạn đang làm gì là một cảnh quay trong bóng tối), nhưng tôi luôn được khuyên nên tránh kiểm tra như thế này, và thay vào đó hãy chọn một lớp khác. Vì vậy, thay vì thực hiện một số kiểm tra và có các hành động khác nhau tùy thuộc vào loại, hãy làm cho lớp biết cách tự xử lý ...

ví dụ: Ob có thể là ISpecialType hoặc IType;

cả hai đều có phương thức DoStuff () được định nghĩa. Đối với IType, nó chỉ có thể quay lại hoặc làm những thứ tùy chỉnh, trong khi ISpecialType có thể làm những thứ khác.

Thao tác này sau đó sẽ loại bỏ hoàn toàn bất kỳ quá trình truyền nào, làm cho mã sạch hơn và dễ bảo trì hơn và lớp biết cách thực hiện các nhiệm vụ của chính nó.


Có, vì tất cả những gì tôi sẽ làm nếu kiểu kiểm tra true là gọi một phương thức giao diện nhất định trên nó, tôi chỉ có thể di chuyển phương thức giao diện đó vào lớp cơ sở và không làm gì theo mặc định. Điều đó có thể thanh lịch hơn việc tạo một hàm ảo để kiểm tra loại.
JubJub

Tôi đã thực hiện một bài kiểm tra tương tự với Binary Worrier sau nhận xét của abatishchev và chỉ tìm thấy sự khác biệt 60ms trên 10.000.000 lần hiển thị.
Ian

1
Wow, cảm ơn vì sự giúp đỡ. Tôi cho rằng bây giờ tôi sẽ sử dụng các toán tử kiểm tra kiểu, trừ khi có vẻ như việc tổ chức lại cấu trúc lớp có vẻ thích hợp. Tôi sẽ sử dụng toán tử 'as' như Andrew đã đề xuất vì tôi không muốn truyền dư thừa.
JubJub

15

Tôi đã thực hiện một phép so sánh hiệu suất về hai khả năng so sánh loại

  1. myobject.GetType () == typeof (MyClass)
  2. myobject là MyClass

Kết quả là: Sử dụng "is" nhanh hơn khoảng 10 lần !!!

Đầu ra:

Thời gian để So sánh Loại: 00: 00: 00.456

Thời gian để so sánh: 00: 00: 00.042

Mã của tôi:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace ConsoleApplication3
{
    class MyClass
    {
        double foo = 1.23;
    }

    class Program
    {
        static void Main(string[] args)
        {
            MyClass myobj = new MyClass();
            int n = 10000000;

            Stopwatch sw = Stopwatch.StartNew();

            for (int i = 0; i < n; i++)
            {
                bool b = myobj.GetType() == typeof(MyClass);
            }

            sw.Stop();
            Console.WriteLine("Time for Type-Comparison: " + GetElapsedString(sw));

            sw = Stopwatch.StartNew();

            for (int i = 0; i < n; i++)
            {
                bool b = myobj is MyClass;
            }

            sw.Stop();
            Console.WriteLine("Time for Is-Comparison: " + GetElapsedString(sw));
        }

        public static string GetElapsedString(Stopwatch sw)
        {
            TimeSpan ts = sw.Elapsed;
            return String.Format("{0:00}:{1:00}:{2:00}.{3:000}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds);
        }
    }
}

13

Điểm Andrew Hare đưa ra về việc hiệu suất bị mất khi bạn thực hiện iskiểm tra và sau đó ép kiểu là hợp lệ nhưng trong C # 7.0, chúng ta có thể làm là kiểm tra khớp mẫu phù thủy để tránh sử dụng thêm sau này:

if (obj is ISpecialType st)
{
   //st is in scope here and can be used
}

Hơn nữa nếu bạn cần kiểm tra giữa nhiều loại cấu trúc đối sánh mẫu C # 7.0 hiện cho phép bạn thực hiện switchtrên các loại:

public static double ComputeAreaModernSwitch(object shape)
{
    switch (shape)
    {
        case Square s:
            return s.Side * s.Side;
        case Circle c:
            return c.Radius * c.Radius * Math.PI;
        case Rectangle r:
            return r.Height * r.Length;
        default:
            throw new ArgumentException(
                message: "shape is not a recognized shape",
                paramName: nameof(shape));
    }
}

Bạn có thể đọc thêm về đối sánh mẫu trong C # trong tài liệu tại đây .


1
Chắc chắn là một giải pháp hợp lệ, nhưng tính năng so khớp mẫu C # này làm tôi buồn, khi nó khuyến khích mã "tính năng ghen tị" như thế này. Chắc chắn chúng ta nên cố gắng đóng gói logic nơi chỉ các đối tượng dẫn xuất "biết" cách tính toán diện tích của chính chúng, và sau đó chúng chỉ trả về giá trị?
Dib

2
SO cần các nút lọc (trên câu hỏi) cho các câu trả lời áp dụng cho các phiên bản mới hơn của khung, nền tảng, v.v. Câu trả lời này tạo cơ sở cho câu trả lời đúng cho C # 7.
Nick Westgate

1
Các lý tưởng @Dib OOP bị ném ra khỏi cửa sổ khi bạn đang làm việc với các kiểu / lớp / giao diện mà bạn không kiểm soát. Cách tiếp cận này cũng hữu ích khi xử lý kết quả của một hàm có thể trả về một trong nhiều giá trị thuộc các kiểu hoàn toàn khác nhau (vì C # vẫn chưa hỗ trợ các kiểu liên hợp - bạn có thể sử dụng các thư viện tương tự OneOf<T...>nhưng chúng có những thiếu sót lớn) .
Đại

4

Trong trường hợp có ai thắc mắc, tôi đã thực hiện các bài kiểm tra trong Unity engine 2017.1, với phiên bản thời gian chạy tập lệnh .NET4.6 (Experimantal) trên một máy tính xách tay có CPU i5-4200U. Các kết quả:

Average Relative To Local Call LocalCall 117.33 1.00 is 241.67 2.06 Enum 139.33 1.19 VCall 294.33 2.51 GetType 276.00 2.35

Toàn bộ bài viết: http://www.ennoble-studios.com/tuts/unity-c-performance-comparison-is-vs-enum-vs-virtual-call.html


Liên kết bài viết đã chết.
James Wilkins

Liên kết @James được hồi sinh.
Gru

Những thứ tốt - nhưng tôi đã không phản đối bạn (thực ra tôi vẫn ủng hộ); Trong trường hợp bạn đang băn khoăn. :)
James Wilkins

-3

Tôi luôn được khuyên là nên tránh kiểm tra như thế này, và thay vào đó là một lớp khác. Vì vậy, thay vì thực hiện một số kiểm tra và có các hành động khác nhau tùy thuộc vào loại, hãy làm cho lớp biết cách tự xử lý ...

ví dụ: Ob có thể là ISpecialType hoặc IType;

cả hai đều có phương thức DoStuff () được định nghĩa. Đối với IType, nó chỉ có thể quay lại hoặc làm những thứ tùy chỉnh, trong khi ISpecialType có thể làm những thứ khác.

Thao tác này sau đó sẽ loại bỏ hoàn toàn bất kỳ quá trình truyền nào, làm cho mã sạch hơn và dễ bảo trì hơn và lớp biết cách thực hiện các nhiệm vụ của chính nó.


1
Điều này không trả lời câu hỏi. Dù sao, các lớp có thể không phải lúc nào cũng biết cách tự xử lý do thiếu ngữ cảnh. Chúng tôi áp dụng logic tương tự để xử lý ngoại lệ khi chúng tôi cho phép các ngoại lệ đi lên chuỗi cuộc gọi cho đến khi một số phương thức / hàm có đủ ngữ cảnh để xử lý lỗi.
Vakhtang
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.