C # 'is'연산자 성능


102

빠른 성능이 필요한 프로그램이 있습니다. 내부 루프 중 하나 내에서 개체의 유형을 테스트하여 특정 인터페이스에서 상속되는지 확인해야합니다.

이를 수행하는 한 가지 방법은 CLR의 기본 제공 형식 검사 기능을 사용하는 것입니다. 가장 우아한 방법은 아마도 'is'키워드 일 것입니다.

if (obj is ISpecialType)

또 다른 접근 방식은 기본 클래스에 미리 정의 된 열거 형 값을 반환하는 내 자신의 가상 GetType () 함수를 제공하는 것입니다 (실제로는 bool 만 필요합니다). 그 방법은 빠르지 만 덜 우아합니다.

특히 'is'키워드에 대한 IL 명령어가 있다고 들었지만 이것이 네이티브 어셈블리로 변환 될 때 빠르게 실행된다는 의미는 아닙니다. 누구든지 다른 방법에 비해 'is'의 성능에 대한 통찰력을 공유 할 수 있습니까?

업데이트 : 정보에 입각 한 모든 답변에 감사드립니다! 답변에 도움이되는 몇 가지 포인트가 흩어져있는 것 같습니다. 자동으로 캐스트를 수행하는 'is'에 대한 Andrew의 포인트는 필수적이지만 Binary Worrier와 Ian이 수집 한 성능 데이터도 매우 유용합니다. 이 모든 정보 를 포함하도록 답변 중 하나를 편집하면 좋을 것 입니다.


2
정말 종류 - 그것은 주요 CLR의 규칙 중 하나 나누기 때문에 BTW, CLR은 당신에게 당신의 자신의 유형 GetType을 () 함수를 만들 수있는 가능성을 제공하지 않습니다
abatishchev

1
어, "진정한 형식"규칙이 무엇을 의미하는지 완전히 잘 모르겠지만 CLR에 기본 제공 Type GetType () 함수가 있다는 것을 이해합니다. 이 방법을 사용하면 열거 형을 반환하는 다른 이름의 함수가 있으므로 이름 / 기호 충돌이 발생하지 않습니다.
JubJub

3
나는 abatishchev가 "유형 안전성"을 의미한다고 생각합니다. GetType ()은 형식이 자신에 대해 거짓말을하는 것을 방지하여 형식 안전성을 유지하기 위해 가상이 아닙니다.
Andrew Hare

2
루프 내에서 수행 할 필요가 없도록 프리 페치 및 유형 준수 캐싱을 고려 ​​했습니까? 모든 perf 질문은 항상 엄청나게 큰 것으로 보이지만 이것은 나에게 c #에 대한 이해가 부족한 것처럼 보입니다. 실제로 너무 느린가요? 어떻게? 당신은 무엇을 시도 했습니까? 분명히 많이 ... 답변에 귀하의 의견을 부여하지
Gusdor

답변:


114

is유형을 확인한 후 해당 유형으로 캐스팅 하면 사용하면 성능이 저하 될 수 있습니다. is실제로 검사중인 유형으로 개체를 캐스팅하므로 후속 캐스팅이 중복됩니다.

어쨌든 캐스팅하려는 경우 다음과 같은 더 나은 방법이 있습니다.

ISpecialType t = obj as ISpecialType;

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

1
감사. 그러나 조건부가 실패 할 경우 객체를 캐스팅하지 않을 경우 가상 함수를 사용하여 유형을 테스트하는 것이 더 나을까요?
JubJub

4
@JubJub : 아니요. 실패는 as기본적으로 is(즉, 유형 검사) 와 동일한 작업을 수행합니다 . 유일한 차이점은 그 다음 반환한다는 것입니다 null대신 false.
Konrad Rudolph

74

Ian 과 함께 있습니다. 아마이 작업을 원하지 않을 것입니다.

그러나 아시다시피, 10,000,000 회가 넘는 두 반복 사이에는 거의 차이가 없습니다.

  • 열거 형 검사는 700 밀리 초 (대략)에 들어옵니다.
  • IS 검사는 1000 밀리 초 (대략)에 들어옵니다.

개인적으로이 문제를 이런 식으로 고치지는 않겠지 만, 한 가지 방법을 선택해야한다면 내장 IS 검사가 될 것입니다. 성능 차이는 코딩 오버 헤드를 고려할 가치가 없습니다.

내 기본 및 파생 클래스

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 : 테스트에 대한 더 많은 정보를 요청했습니다.

콘솔 앱 (디버그 빌드)에서 두 테스트를 모두 실행했습니다. 각 테스트는 다음과 같습니다.

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

릴리스에서 실행하면 Ian처럼 60-70ms의 차이가 발생합니다.

추가 업데이트-2012 년 10 월 25 일
후 나는 이것에 대해 알아 차렸고, 컴파일러는 bool b = a is MyClassBb가 어디에도 사용되지 않기 때문에 릴리스 에서 생략하도록 선택할 수 있습니다 .

이 코드. . .

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

. . . 일관되게 is확인이 약 57 밀리 초에 수신되고 열거 형 비교가 29 밀리 초에 수신됨을 보여줍니다.

NB 여전히 is수표를 선호합니다 . 차이가 너무 작아서 신경 쓸 수 없습니다.


35
+1은 가정하는 대신 실제로 성능을 테스트합니다.
Jon Tackabury

3
그것은 매우 고가 인 대신 DateTime.Now의, 스톱워치 클래스와 테스트를하는 것이 훨씬 낫다
abatishchev

2
나는 그것을 기내에 가져갈 것이지만, 이번 경우에는 그것이 결과에 영향을 미칠 것이라고 생각하지 않습니다. 감사합니다 :)
Binary Worrier

11
@Binary Worrier- 클래스 의 새로운 연산자 할당은 'is'작업의 성능 차이를 완전히 가릴 것입니다. 두 개의 서로 다른 사전 할당 된 인스턴스를 재사용하여 이러한 작업 을 제거한 다음 코드를 다시 실행하고 결과를 게시 하는 것이 어떻습니까?

1
@mcmillab : 어떤 작업을하던간에 is운영자가 초래 하는 성능 저하보다 훨씬 더 큰 병목 현상 이 발생하고 is운영자 주변의 설계 및 코딩에 대해 들으면 많은 비용이 듭니다. 코드 품질과 궁극적으로 성능면에서 자기 패배가 될 것입니다. 이 경우 나는 내 진술을지지한다. '는'연산자입니다 결코 될 것 없다 런타임 성능에 문제가 있습니다.
Binary Worrier

23

그래서 누군가와 이것에 대해 이야기하고 있었고 이것을 더 테스트하기로 결정했습니다. 내가 말할 수있는 한, as및 의 성능은 is유형 정보를 저장하기 위해 자체 멤버 또는 함수를 테스트하는 것과 비교할 때 매우 좋습니다.

Stopwatch방금 배운을 사용 했지만 가장 신뢰할 수있는 접근 방식이 아닐 수 있으므로 UtcNow. 나중에 UtcNow예측할 수없는 생성 시간 을 포함 하는 것과 유사한 프로세서 시간 접근 방식도 시도 했습니다. 또한 가상 클래스가없는 기본 클래스를 비 추상적으로 만들려고 시도했지만 큰 효과가없는 것 같습니다.

나는 이것을 16GB RAM의 Quad Q6600에서 실행했습니다. 50mil 반복에도 불구하고 숫자는 여전히 +/- 50 밀리 초 정도 튀어 나오므로 사소한 차이를 너무 많이 읽지 않을 것입니다.

x64가 더 빨리 생성되었지만 x86보다 느리게 실행되는 것을 보는 것은 흥미로 웠습니다.

x64 릴리스 모드 :
스톱워치 :
As : 561ms
Is : 597ms
기본 속성 : 539ms
기본 필드 : 555ms
기본 RO 필드 : 552ms
Virtual GetEnumType () 테스트 : 556ms
Virtual IsB () 테스트 : 588ms
생성 시간 : 10416ms

UtcNow :
As : 499ms
Is : 532ms
기본 속성 : 479ms
기본 필드 : 502ms
Base RO 필드 : 491ms
Virtual GetEnumType () : 502ms
Virtual bool IsB () : 522ms
생성 시간 : 285ms (이 숫자는 UtcNow에서 신뢰할 수없는 것 같습니다. 나도 109ms 및 806ms.)

x86 릴리스 모드 :
스톱워치 :
As : 391ms
Is : 423ms
기본 속성 : 369ms
기본 필드 : 321ms
기본 RO 필드 : 339ms
Virtual GetEnumType () 테스트 : 361ms
Virtual IsB () 테스트 : 365ms
생성 시간 : 14106ms

UtcNow :
As : 348ms
Is : 375ms
기본 속성 : 329ms
기본 필드 : 286ms
기본 RO 필드 : 309ms
Virtual GetEnumType () : 321ms
Virtual bool IsB () : 332ms
생성 시간 : 544ms (이 숫자는 UtcNow에서 신뢰할 수없는 것 같습니다.)

대부분의 코드는 다음과 같습니다.

    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
(일부 보너스 오전 5시에서 영감을 얻은 셰익스피어 ...) 될 것인가 말 것인가 : 그것이 질문이다. 언어 학자 그리고 그것의 지시를 불러서 그들을 신뢰합니까? 추측하다 : 궁금해하다; 더 이상은 없어; 그리고 시간에 얽매이는 코더들이 상속받는 수천 가지의 잠재 의식과 두통을 식별하는 타이밍에 의해 끝납니다. '폐쇄는 경건한 바램'd. 죽는 것이 아니라 잠이 든다. 그래, 나는 잠을 잘 것이다. 꿈을 꾸는 것은 가장 기초적인 계급에서 파생 될 수있는 것과 같다.
Jared Thirsk

이것으로부터 속성에 접근하는 것이 x64에서 필드에 접근하는 것보다 빠르다는 결론을 내릴 수 있습니까? 이게 어떻게 될 수 있는지 나에게 놀라운 일 이니까?
Didier A.

1
나는 결론을 내리지 않을 것입니다. 왜냐하면 "50mil 반복에도 불구하고, 숫자는 여전히 +/- 50 밀리 초 정도에서 튕겨 져서 사소한 차이를 너무 많이 읽지 않을 것입니다."
Jared Thirsk dec.

16

앤드류가 맞습니다. 실제로 코드 분석을 사용하면 Visual Studio에서 불필요한 캐스트로보고됩니다.

한 가지 아이디어 (당신이 무엇을하고 있는지 모르는 것은 어둠 속에서 약간의 기회입니다),하지만 저는 항상 이와 같은 검사를 피하고 대신 다른 클래스를 갖는 것이 좋습니다. 따라서 몇 가지 검사를 수행하고 유형에 따라 다른 작업을 수행하는 대신 클래스가 자신을 처리하는 방법을 알도록하십시오.

예를 들어 Obj는 ISpecialType 또는 IType 일 수 있습니다.

둘 다 DoStuff () 메서드가 정의되어 있습니다. IType의 경우 반환하거나 사용자 지정 작업을 수행 할 수 있지만 ISpecialType은 다른 작업을 수행 할 수 있습니다.

그러면 캐스팅이 완전히 제거되고 코드가 더 깨끗하고 유지 관리가 쉬워지며 클래스는 자체 작업을 수행하는 방법을 알고 있습니다.


예, 유형 테스트가 true 인 경우 수행 할 작업은 특정 인터페이스 메서드를 호출하는 것이므로 해당 인터페이스 메서드를 기본 클래스로 이동하고 기본적으로 아무것도하지 않도록 할 수 있습니다. 유형을 테스트하기 위해 가상 함수를 만드는 것보다 더 우아 할 수 있습니다.
JubJub

abatishchev의 의견 후에 Binary Worrier와 유사한 테스트를 수행 한 결과 10,000,000 회에서 60ms 차이 만 발견되었습니다.
Ian

1
와, 도와 주셔서 감사합니다. 클래스 구조를 재구성하는 것이 적절 해 보이지 않는 한 지금은 유형 검사 연산자를 계속 사용한다고 가정합니다. 중복 캐스트를 원하지 않으므로 Andrew가 제안한대로 'as'연산자를 사용하겠습니다.
JubJub

15

유형 비교의 두 가지 가능성에 대해 성능 비교를 수행했습니다.

  1. myobject.GetType () == typeof (MyClass)
  2. myobject는 MyClass입니다.

결과 : "is"를 사용하는 것이 약 10 배 더 빠릅니다 !!!

산출:

유형 비교 시간 : 00 : 00 : 00.456

Is-Comparison 시간 : 00 : 00 : 00.042

내 코드 :

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

Andrew Hare가 is검사 를 수행 할 때 성능 손실에 대해 말한 다음 캐스트가 유효했지만 C # 7.0에서는 나중에 추가 캐스트를 피하기 위해 마녀 패턴 일치를 검사 할 수 있습니다.

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

여러 유형 사이를 확인해야하는 경우 더 많은 C # 7.0 패턴 일치 구문을 사용 switch하여 유형에 대해 수행 할 수 있습니다.

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

여기 문서에서 C #의 패턴 일치에 대해 자세히 알아볼 수 있습니다 .


1
확실히 유효한 솔루션이지만이 C # 패턴 일치 기능은 이와 같은 "기능 부러움"코드를 장려 할 때 나를 슬프게합니다. 확실히 우리는 파생 된 객체 만이 자신의 영역을 계산하는 방법을 "알고"값을 반환하는 논리의 캡슐화를 위해 노력해야합니까?
Dib

2
따라서 새로운 버전의 프레임 워크, 플랫폼 등에 적용되는 답변에 대한 필터 버튼 (질문에)이 필요합니다.이 답변은 C # 7에 대한 올바른 답변의 기초를 형성합니다.
Nick Westgate

1
@Dib OOP 이상은 제어하지 않는 유형 / 클래스 / 인터페이스로 작업 할 때 창 밖으로 던져집니다. 이 접근 방식은 완전히 다른 유형의 여러 값 중 하나를 반환 할 수있는 함수의 결과를 처리 할 때에도 유용합니다 (C #은 아직 공용체 유형을 아직 지원하지 않기 때문에-같은 라이브러리를 사용할 수 OneOf<T...>있지만 큰 단점이 있습니다) .
Dai

4

궁금한 점이 있으시면 Unity 엔진 2017.1에서 i5-4200U CPU가 탑재 된 노트북에서 스크립팅 런타임 버전 .NET4.6 (Experimantal)을 사용하여 테스트했습니다. 결과 :

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

전체 기사 : http://www.ennoble-studios.com/tuts/unity-c-performance-comparison-is-vs-enum-vs-virtual-call.html


기사 링크가 종료되었습니다.
James Wilkins

@James 링크가 부활했습니다.
Gru

좋은 것들-하지만 나는 당신을 비추천하지 않았습니다 (실제로 나는 어쨌든 찬성했습니다); 궁금한 경우. :)
James Wilkins

-3

나는 항상 이와 같은 검사를 피하고 대신 다른 클래스를 갖는 것이 좋습니다. 따라서 몇 가지 검사를 수행하고 유형에 따라 다른 작업을 수행하는 대신 클래스가 자신을 처리하는 방법을 알도록하십시오.

예를 들어 Obj는 ISpecialType 또는 IType 일 수 있습니다.

둘 다 DoStuff () 메서드가 정의되어 있습니다. IType의 경우 반환하거나 사용자 지정 작업을 수행 할 수 있지만 ISpecialType은 다른 작업을 수행 할 수 있습니다.

그러면 캐스팅이 완전히 제거되고 코드가 더 깨끗하고 유지 관리가 쉬워지며 클래스는 자체 작업을 수행하는 방법을 알고 있습니다.


1
이것은 질문에 대한 답이 아닙니다. 어쨌든, 수업은 컨텍스트 부족으로 인해 항상 자신을 처리하는 방법을 알지 못할 수 있습니다. 일부 메서드 / 함수가 오류를 처리하기에 충분한 컨텍스트를 가질 때까지 예외가 호출 체인으로 올라가도록 허용 할 때 예외 처리에 유사한 논리를 적용합니다.
Vakhtang
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.