2011-09-08 10 views
2

생성자의 인수에 따라 특수 클래스 동작을 작성해야합니다. 녹색 연필로 건축 (그려진)했다면 푸 그림은 영원히 녹색이되어야한다고합니다. 연필을 사용하는 경우 Foo는 투명해야합니다.생성자 우선 순위

이제 코드를보십시오. 생성자가 전달 된 매개 변수 객체의 실제 유형을 "참조"하는 "출력"을 수정할 가능성이 있습니까? (실제로는 "개체"의 모두) :

class Program 
{ 
    static void Main(string[] args) 
    { 
     object[] objs = { new IndexOutOfRangeException(), MyEnum.Beta, 45, new AssemblyName(), new { Name = "a" } }; 

     for (int i = 0; i < objs.Length; i++) 
     { 
      Console.WriteLine("{0} => {1} ", i, objs[i]); 
     } 
     Console.WriteLine("=========================== "); 
     for (int i = 0; i < objs.Length; i++) 
     { 
      Foo myFoo = new Foo(objs[i]); 
      Console.WriteLine("{0} => {1}", i, myFoo); 
     } 
    } 
} 

public class Foo 
{ 
    object value; 
    string typeName; 

    public Foo(object obj) 
    { 
     value = obj; 
     typeName = "object"; 
    } 

    public Foo(MyEnum enm) 
    { 
     value = enm; 
     typeName = "MyEnum"; 
    } 

    public Foo(int myInt) 
    { 
     value = myInt; 
     typeName = "int"; 
    } 

    public Foo(Exception ex) 
    { 
     value = ex; 
     typeName = "exception"; 
    } 

    public override string ToString() 
    { 
     return string.Format("FOO (object = '{0}'; type = '{1}')", value, typeName); 
    } 
} 

public enum MyEnum 
{ 
    Alpha, 
    Beta 
} 

OUTPUT

0 => System.IndexOutOfRangeException: Index was outside the bounds of the array. 
1 => Beta 
2 => 45 
3 => 
4 => { Name = a } 

=========================== 

0 => FOO (object = 'System.IndexOutOfRangeException: Index was outside the bound 
s of the array.'; type = 'object') 
1 => FOO (object = 'Beta'; type = 'object') 
2 => FOO (object = '45'; type = 'object') 
3 => FOO (object = ''; type = 'object') 
4 => FOO (object = '{ Name = a }'; type = 'object') 

편집 : 몇 가지 답변을 참조로

것은, 내가 그 올바른에 대해 아닙니다 강조하고 싶습니다 문자열을 "type"변수에 표시하려면 value.GetType()을 사용하는 것과 같지만 올바른 생성자에 "입력"하는 것이 문제입니다.

다른 말로하면, 컴파일러가 올바른 유형을 감지하지 못하고 올바른 생성자로 "리디렉션"합니까?

편집 2 : 답변자에서 언급 한 바와 같이

, 생성자에 "방법"컴파일시가 아닌 런타임에 "내장"입니다. 말하는 그의

이 이
MyEnum en = MyEnum.Beta; 
Console.WriteLine("Enum example: obj:{0} Foo:{1}", en, new Foo(en)); 
이 이 이

윌 출력이 "좋은"출력과 같은 코드 :

Enum example: obj:Beta Foo:FOO (object = 'Beta'; type = 'MyEnum') 

그래서 ...이 분명히, 어떤 방법을 "우회"이 동작하지만 생성자의 런타임 감지가 제안처럼 리드 copsey ...?!

+0

런타임까지 유형을 알 수 없기 때문에 컴파일러에서 유형을 감지 할 수 없습니다. 따라서 올바른 cctor를 결정할 수 없습니다. –

+0

@David Neale : 감사합니다. 내가 "우회"하는 해결 방법이 필요합니다.) – serhio

+0

리플렉션 ('GetType()')을 사용하여 유형 이름을 가져 오는 것이 좋습니다. 당신이 런타임에 도착하면 그게 당신이 할 수있는 유일한 방법입니다. 그렇지 않으면 - 왜이 객체들을 통해 반복하고 있습니까? 그게 필요 할까? –

답변

3

컴파일러가 올바른 형식을 감지하지 못하고 올바른 생성자로 "리디렉션"되는 이유는 무엇입니까?

이것은 객체를 System.Object으로 전달하기 때문입니다. (object[] objs = { n...) 런타임시 컴파일러가 일 때 생성자가 으로 선택됩니다. 사용 된 변수의 선언은 컴파일러에서 볼 수 있으며 적절한 유형을 확인하는 데 사용됩니다. 다른 의견에

언급 한 바와 같이 :

좋아, 내가 개체의 큰 배열을 가지고 있고, 모르는 경우, 선험적으로 자신의 유형을?

이것은 정확히 컴파일러가 이러한 방식으로 작동하는 이유입니다. 컴파일 타임에 원하는 생성자를 알 수 없으며, 작동하는 생성자가 System.Object이므로 선택됩니다.

이러한 특정 유형을 별도로 처리하면서도 개체를 System.Object으로 계속 구성하려면 개체 생성자 내부에서 해당 개체의 검사를 추가하고 특정 사례를 별도로 처리해야합니다. 그러나 이렇게하면 유지 관리가 가장 쉬운 코드는 아닙니다.

public Foo(object obj) 
{ 
    value = obj; 
    typeName = "object"; 

    // Change typeName if appropriate 
    if (obj != null) 
    { 
     if (obj is MyEnum) 
      typeName = "MyEnum"; 
     else if (obj is int) 
      typeName = "int"; 
     else if (obj is Exception) 
      typeName = "exception"; 
    } 
} 

편집 : 실제 코드에서 생성자 가능성이 더 많은 일을 할 것입니다, 점을 감안

, 나는이 문제를 처리하기 위해 공장 방법을 고려할 것입니다.

// I'd make the object constructor private, to prevent accidental usage: 
private Foo(object obj) { ... 

public static Foo CreateAppropriateFoo(object obj) 
{ 
    if (obj == null) 
     return new Foo(obj); // Use object constructor 
    else 
    {    
     if (obj is MyEnum) 
      return new Foo((MyEnum)obj); 
     else if (obj is int) 
      return new Foo((int)obj); 
     else if (obj is Exception) 
      return new Foo((Exception)obj); 
    } 
} 

이, 적어도, 생성자 논리의 중복을 방지 할뿐만 아니라, 조금 더 분명하게 : 그것은 당신이 위와 유사한 방법을 사용하지만, 대신에 형에게 안전 생성자를 떠날 수있는 것 런타임에 약간의 논리가 발생한다는 것입니다.

+0

그래서, 당신은 하나의 크고 일반적인 모든 특정 생성자를 대체 제안 ... 나는 그것을 피할 수 있다고 생각 ... – serhio

+0

@ serhio : 나는 반드시 그들을 대체하지 않을거야,하지만 원한다면 'Object'를 전달하고 타입을 결정할 수 있으려면 런타임에 체크해야합니다. 컴파일러는 위에서 언급 한 이유로이 문제를 처리하지 않습니다. 실제 생성자 (Exception 또는 서브 클래스로 선언 된 예외)를 전달할 때 컴파일러가이를 올바르게 파악할 수 있으므로 다른 생성자를 남겨두면 괜찮습니다. –

+0

예, 그런 식으로 코드를 복제합니다. 마찬가지로 (잘못) Devendra D. 제안Chavan, 불행히도, 코드에서 코드를 복사하지 않기 위해 다른 클래스의 생성자를 사용할 수는 없습니다 ... – serhio

0

개체 매개 변수를 받아들이는 ctor (모든 것이 적절한 생성자에 올바르게 바인딩되어야 함)를 제거하거나 매개 변수를 적절한 형식 (예 : this)으로 캐스팅하여 얻을 수 있다고 생각합니다.

var myFoo = new Foo(objs[0] as Exception); 

은 대부분 예외 매개 변수와 함께 ctor를 사용합니다.

편집 : 질문 : 왜 컴파일러가 올바른 유형을 감지하지 않고 올바른 생성자에 "리디렉션"?

내 대답 : 컴파일러가 개체로 그 실제 개체 인스턴스를 취급 바로을 그래서 당신은 객체 배열의 멤버를 통과하기 때문에.

가장 일반적인 것부터 일반적인 것까지 순서대로 정렬해야하는 catch 블록과 비슷합니다. 상단에 일반 포수가있어서 객체를 받아들이는 것이 중요하며 객체의 값을 전달하면 놀라지 않을 것입니다. array 컴파일러는 가장 적합한 후보를 사용합니다. 그렇기 때문에 ctor 호출에 대한 캐스트를 제안 했으므로 objs [0]을 Exception 객체로 캐스팅하려는 것으로 명시 적으로 말할 수 있습니다. 그렇지 않으면 컴파일러에서 객체로 간주합니다.

+0

좋아요, 만약 내가 큰 배열의 객체를 가지고 있고, 알지 못한다면, * 선험적 * 유형입니까? – serhio

+0

당신은 바람에 맞서 싸우고 있습니다. 아무 승자가 없습니다 ... –