programing tip

'ref'와 'out'이 다형성을 지원하지 않는 이유는 무엇입니까?

itbloger 2020. 7. 10. 08:08
반응형

'ref'와 'out'이 다형성을 지원하지 않는 이유는 무엇입니까?


다음을 수행하십시오.

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

위의 컴파일 타임 오류가 발생하는 이유는 무엇입니까? 이것은 인수 refout인수 모두에서 발생합니다 .


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

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

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

이 문제에 대한 자세한 설명은 블로그 페이지를 참조하십시오. 좋은 질문 감사합니다.

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

의 당신이 수업을 가정하자 Animal, Mammal, Reptile, Giraffe, TurtleTiger, 명백한 서브 클래스의 관계.

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


당신은 유형의 변수를 전달할 수 AnimalM?

아니요. 해당 변수는을 포함 할 수 Turtle있지만 MMammals 만 포함한다고 가정합니다. A TurtleMammal입니다.

결론 1 : ref매개 변수를 "더 크게"만들 수 없습니다. (포유류보다 포유류가 많으므로 더 많은 것을 포함 할 수 있기 때문에 변수가 더 큽니다.)


당신은 유형의 변수를 전달할 수 GiraffeM?

번호 M에 기록 할 수있는 m, 그리고 M된 기록 할 수 있습니다 Tiger로를 m. 이제 Tiger실제로 유형 인 변수 에을 넣었습니다 Giraffe.

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


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

당신은 유형의 변수를 전달할 수 GiraffeN?

번호 N에 쓸 수 nN를 작성 할 수 있습니다 Tiger.

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


당신은 유형의 변수를 전달할 수 AnimalN?

흠.

왜 안돼? N에서 읽을 수없고 n쓰기 만 할 수 있습니다. Tiger타입의 변수에 a 쓰면 Animal모든 것이 설정됩니다.

잘못된. 규칙은 " N쓸 수 없습니다" 는 아닙니다 n.

규칙은 다음과 같습니다.

1) 반품 전에 정상적으로 N써야 합니다. (경우 가 발생, 모든 베팅은 꺼져 있습니다.)nNN

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

이를 통해 다음과 같은 일련의 이벤트가 가능합니다.

  • Declare a field x of type Animal.
  • Pass x as an out parameter to N.
  • N writes a Tiger into n, which is an alias for x.
  • On another thread, someone writes a Turtle into x.
  • N attempts to read the contents of n, and discovers a Turtle in what it thinks is a variable of type Mammal.

Clearly we want to make that illegal.

Conclusion 4: out parameters cannot be made "larger".


Final conclusion: Neither ref nor out parameters may vary their types. To do otherwise is to break verifiable type safety.

If these issues in basic type theory interest you, consider reading my series on how covariance and contravariance work in C# 4.0.


Because in both cases you must be able to assign value to ref/out parameter.

If you try to pass b into Foo2 method as reference, and in Foo2 you try to assing a = new A(), this would be invalid.
Same reason you can't write:

B b = new A();

You're struggling with the classic OOP problem of covariance (and contravariance), see wikipedia: much as this fact may defy intuitive expectations, it's mathematically impossible to allow substitution of derived classes in lieu of base ones for mutable (assignable) arguments (and also containers whose items are assignable, for just the same reason) while still respecting Liskov's principle. Why that is so is sketched in the existing answers, and explored more deeply in these wiki articles and links therefrom.

OOP languages that appear to do so while remaining traditionally statically typesafe are "cheating" (inserting hidden dynamic type checks, or requiring compile-time examination of ALL sources to check); the fundamental choice is: either give up on this covariance and accept practitioners' puzzlement (as C# does here), or move to a dynamic typing approach (as the very first OOP language, Smalltalk, did), or move to immutable (single-assignment) data, like functional languages do (under immutability, you can support covariance, and also avoid other related puzzles such as the fact that you cannot have Square subclass Rectangle in a mutable-data world).


Consider:

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

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

B b = null;
Foo2(ref b);

It would violate type-safety


While the other responses have succinctly explained the reasoning behind this behavior, I think it's worth mentioning that if you really need to do something of this nature you can accomplish similar functionality by making Foo2 into a generic method, as such:

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

Because giving Foo2 a ref B would result in a malformed object because Foo2 only knows how to fill A part of B.


Isn't that the compiler telling you it would like you to explicitly cast the object so that it can be sure you know what your intentions are?

Foo2(ref (A)b)

Makes sense from a safety perspective, but I would have preferred it if the compiler gave a warning instead of an error, since there are legitimate uses of polymoprhic objects passed by reference. e.g.

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

This won't compile, but would it work?


If you use practical examples for your types, you'll see it:

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

And now you have your function that takes the ancestor (i.e. Object):

void Foo2(ref Object connection) { }

What can possibly be wrong with that?

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

You just managed to assign a Bitmap to your SqlConnection.

That's no good.


Try again with others:

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

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

You stuffed an OracleConnection over-top of your SqlConnection.


In my case my function accepted an object and I couldn't send in anything so I simply did

object bla = myVar;
Foo(ref bla);

And that works

My Foo is in VB.NET and it checks for type inside and does a lot of logic

I apologize if my answer is duplicate but others were too long

참고URL : https://stackoverflow.com/questions/1207144/why-doesnt-ref-and-out-support-polymorphism

반응형