문제

다음을 수행하십시오.

class A {}

class B : A {}

class C
{
    C()
    {
        var b = new B();
        Foo(b);
        Foo2(ref b); // <= compile-time error: 
                     // "The 'ref' argument doesn't match the parameter type"
    }

    void Foo(A a) {}

    void Foo2(ref A a) {}  
}

위의 컴파일 타임 오류가 발생하는 이유는 무엇입니까? 이것은 둘 다에서 발생합니다 ref 그리고 out 논쟁.

도움이 되었습니까?

해결책

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

업데이트 :이 답변을이 블로그 항목의 기초로 사용했습니다.

REF 및 OUT 매개 변수가 유형 변동을 허용하지 않는 이유는 무엇입니까?

이 문제에 대한 자세한 내용은 블로그 페이지를 참조하십시오. 위대한 질문에 감사드립니다.

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

수업이 있다고 가정 해 봅시다 Animal, Mammal, Reptile, Giraffe, Turtle 그리고 Tiger, 명백한 서브 클래스 관계와 함께.

이제 방법이 있다고 가정 해 봅시다 void M(ref Mammal m). M 읽고 쓸 수 있습니다 m.


변수 유형을 전달할 수 있습니까? Animal 에게 M?

아니요. 그 변수는 a를 포함 할 수 있습니다 Turtle, 하지만 M 포유류 만 포함한다고 가정합니다. ㅏ Turtle 아닙니다 Mammal.

결론 1: ref 매개 변수는 "더 큰"것을 만들 수 없습니다. (포유류보다 더 많은 동물이 있으므로 변수는 더 많은 것을 포함 할 수 있기 때문에 "더 커집니다.)


변수 유형을 전달할 수 있습니까? Giraffe 에게 M?

아니. M 글을 쓸 수 있습니다 m, 그리고 M 글을 쓰고 싶을 수도 있습니다 Tiger ~ 안으로 m. 이제 당신은 a를 넣었습니다 Tiger 실제로 유형 인 변수로 Giraffe.

결론 2: ref 매개 변수는 "더 작게"만들 수 없습니다.


이제 고려하십시오 N(out Mammal n).

변수 유형을 전달할 수 있습니까? Giraffe 에게 N?

아니. N 글을 쓸 수 있습니다 n, 그리고 N 글을 쓰고 싶을 수도 있습니다 Tiger.

결론 3: out 매개 변수는 "더 작게"만들 수 없습니다.


변수 유형을 전달할 수 있습니까? Animal 에게 N?

흠.

글쎄, 왜 안돼? N 읽을 수 없습니다 n, 그것은 단지 그것에 쓸 수 있습니까? 당신은 a Tiger 변수 유형으로 Animal 그리고 당신은 모두 설정되어 있습니까?

잘못된. 규칙은 "" "N 쓸 수 있습니다 n".

규칙은 간단히 다음과 같습니다.

1) N 편지를 써야합니다 n ~ 전에 N 정상적으로 반환됩니다. (만약에 N 던지기, 모든 베팅은 꺼져 있습니다.)

2) N 무언가를 써야합니다 n 그것이 무언가를 읽기 전에 n.

이 일련의 이벤트를 허용합니다.

  • 필드를 선언하십시오 x 유형의 Animal.
  • 통과하다 x 로서 out 매개 변수 N.
  • N a Tiger ~ 안으로 n, 그것은 별명입니다 x.
  • 다른 스레드에서 누군가가 a Turtle ~ 안으로 x.
  • N 내용을 읽으려고 시도합니다 n, 그리고 발견 a Turtle 그것이 생각하는 것은 유형의 변수입니다 Mammal.

분명히 우리는 그것을 불법으로 만들고 싶습니다.

결론 4: out 매개 변수는 "더 큰"것으로 만들 수 없습니다.


마지막 결론: 어느 것도 아니다 ref ...도 아니다 out 매개 변수는 해당 유형을 변경할 수 있습니다. 그렇지 않으면 검증 가능한 유형 안전을 깨는 것입니다.

기본 유형 이론의 이러한 문제가 관심을 보이면 읽기를 고려하십시오. C# 4.0에서 공분산 및 비밀화가 어떻게 작동하는지에 대한 나의 시리즈.

다른 팁

두 경우 모두 ref/out 매개 변수에 값을 할당 할 수 있어야합니다.

B를 참조로 FOO2 메소드로 전달하려고하면 FOO2에서 A = New A ()를 주장하려고하면 유효하지 않습니다.
당신이 쓸 수없는 것과 같은 이유 :

B b = new A();

당신은 고전적인 OOP 문제로 어려움을 겪고 있습니다 공분산 (및 비밀), 참조 위키 백과:이 사실이 직관적 인 기대를 무시할 수 있듯이, 수학적으로도 파생 된 클래스를 기본 클래스 대신에 변호 가능한 (할당 가능한) 인수 (및 동일한 이유로 항목이 할당 할 수있는 컨테이너)를 대신하여 여전히 존중하는 것은 불가능합니다. Liskov의 원칙. 그것이 기존의 답변에서 스케치되어 왜이 위키 기사와 링크에서 더 깊이 탐구됩니다.

전통적으로 정적으로 TypeSafe를 유지하면서 그렇게하는 것으로 보이는 OOP 언어는 "속임수"(숨겨진 동적 유형 검사를 삽입하거나 모든 소스의 컴파일 시간 시험이 필요함); 근본적인 선택은 다음과 같습니다.이 공분산을 포기하고 실무자의 당황을 받아들이거나 (C#이 여기에서와 같이) 동적 타이핑 접근법 (최초의 OOP 언어, SmallTalk, DID)으로 이동하거나 불변 (단일-로 이동합니다. 과제) 데이터는 기능 언어와 같은 데이터 (불변성 하에서 공분산을 지원할 수 있으며, 변이 가능한 데이터 세계에서 제곱 서브 클래스 사각형을 가질 수 없다는 사실과 같은 다른 관련 퍼즐을 피할 수 있음).

고려하다:

class C : A {}
class B : A {}

void Foo2(ref A a) { a = new C(); } 

B b = null;
Foo2(ref b);

유형 안전성을 위반합니다

다른 응답은이 행동의 배후에 대한 추론을 간결하게 설명했지만,이 특성에 대해 실제로해야한다면 Foo2를 일반적인 방법으로 만들어 비슷한 기능을 수행 할 수 있다고 언급 할 가치가 있다고 생각합니다.

class A {}

class B : A {}

class C
{
    C()
    {
        var b = new B();
        Foo(b);
        Foo2(ref b); // <= no compile error!
    }

    void Foo(A a) {}

    void Foo2<AType> (ref AType a) where AType: A {}  
}

기부하기 때문에 Foo2ref B 기형 객체가 발생합니다 Foo2 채우는 방법 만 알고 있습니다 A 부분의 B.

컴파일러가 당신이 당신의 의도가 무엇인지 알 수 있도록 객체를 명시 적으로 캐스팅하기를 원한다고 말하지 않습니까?

Foo2(ref (A)b)

안전 관점에서 의미가 있지만, 컴파일러가 오류 대신 경고를 한 경우 선호했을 것입니다. 예를 들어

class Derp : interfaceX
{
   int somevalue=0; //specified that this class contains somevalue by interfaceX
   public Derp(int val)
    {
    somevalue = val;
    }

}


void Foo(ref object obj){
    int result = (interfaceX)obj.somevalue;
    //do stuff to result variable... in my case data access
    obj = Activator.CreateInstance(obj.GetType(), result);
}

main()
{
   Derp x = new Derp();
   Foo(ref Derp);
}

이것은 컴파일하지 않지만 작동합니까?

유형에 대한 실제 예제를 사용하면 다음을 볼 수 있습니다.

SqlConnection connection = new SqlConnection();
Foo(ref connection);

그리고 이제 당신은 당신의 기능을 가지고 있습니다 선조 ( Object):

void Foo2(ref Object connection) { }

그것에 무엇이 잘못되었을 수 있습니까?

void Foo2(ref Object connection)
{
   connection = new Bitmap();
}

방금 할당 할 수있었습니다 Bitmap 너의 ~에게 SqlConnection.

좋지 않습니다.


다른 사람들과 다시 시도하십시오.

SqlConnection conn = new SqlConnection();
Foo2(ref conn);

void Foo2(ref DbConnection connection)
{
    conn = new OracleConnection();
}

당신은 an을 채웠습니다 OracleConnection 당신의 과잉 SqlConnection.

내 경우 내 기능은 객체를 받아 들였고 아무것도 보낼 수 없었기 때문에 단순히

object bla = myVar;
Foo(ref bla);

그리고 그것은 작동합니다

내 foo는 vb.net에 있고 내부 유형을 확인하고 많은 논리를 수행합니다.

내 대답이 중복되면 사과하지만 다른 사람들은 너무 길다면 사과드립니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top