C#에서 매개 변수 앞에서 "ref"를 어디에서 사용합니까?

StackOverflow https://stackoverflow.com/questions/804196

  •  03-07-2019
  •  | 
  •  

문제

"Ref"및 "Out"매개 변수의 정의에 이미 여러 가지 질문이 있지만 디자인이 좋지 않은 것처럼 보입니다. 심판이 올바른 솔루션이라고 생각하는 경우가 있습니까?

항상 더 깨끗한 다른 일을 할 수있는 것 같습니다. 누군가가 문제에 대한 "가장 좋은"솔루션이 어디에 있는지에 대한 예를 제시 할 수 있습니까?

도움이 되었습니까?

해결책

제 생각에는 ref 새로운 유틸리티 유형을 선언하는 어려움과 기존 정보에 대한 "정보를 다루는"어려움에 대해 크게 보상했습니다.이 정보는 LINQ, 제네릭 및 익명 유형을 통해 Genesis 이후에 해결하기 위해 큰 조치를 취한 것입니다.

그래서 아니요, 더 이상 명확한 사용 사례가 많이 있다고 생각하지 않습니다. 나는 그것이 언어가 원래 디자인 된 방식의 유물이라고 생각합니다.

함수뿐만 아니라 반환 값에서 어떤 종류의 오류 코드를 반환 해야하는 경우 (위에서 언급 한 것처럼) 여전히 의미가 있다고 생각하지만 다른 것은 없습니다 (따라서 더 큰 유형은 실제로 정당화되지는 않습니다. ) 프로젝트에서 모든 곳 에서이 작업을 수행한다면 아마도 Thing-Plus-Code의 일반 래퍼 유형을 정의 할 것입니다. ref 그리고 out 괜찮습니다.

다른 팁

잘, ref 일반적으로 전문화 된 경우에는 사용되지만 중복 또는 C#의 레거시 기능이라고 부르지 않습니다. 당신은 그것을 볼 수 있습니다 (그리고 out) 예를 들어 XNA에서 많이 사용했습니다. XNA에서, a Matrix a struct 그리고 다소 거대한 것 (64 바이트를 믿는다). 일반적으로 사용하는 기능으로 전달하는 것이 가장 좋습니다. ref 64 바이트를 복사하지 않으려면 4 또는 8에 불과합니다. 전문가 C# 기능? 틀림없이. 더 이상 사용하지 않거나 나쁜 디자인을 표시하지 않습니까? 동의하지 않습니다.

한 영역은 다음과 같은 작은 유틸리티 기능을 사용하는 것입니다.

void Swap<T>(ref T a, ref T b) { T tmp = a; a = b; b = tmp; }  

여기에는 '청소기'대안이 보이지 않습니다. 물론 이것은 정확히 아키텍처 수준이 아닙니다.

P/Invoke is the only place I can really think of a spot where you must use ref or out. Other cases, they can be convenient, but like you said, there is generally another, cleaner way.

What if you wanted to return multiple objects, that for some unknown reason are not tied together into a single object.

void GetXYZ( ref object x, ref object y, ref object z);

EDIT: divo suggested using OUT parameters would be more appropriate for this. I have to admit, he's got a point. I'll leave this answer here as a, for the record, this is an inadaquate solution. OUT trumps REF in this case.

I think the best uses are those that you usually see; you need to have both a value and a "success indicator" that is not an exception from a function.

One design pattern where ref is useful is a bidirectional visitor.

Suppose you had a Storage class that can be used to load or save values of various primitive types. It is either in Load mode or Save mode. It has a group of overloaded methods called Transfer, and here's an example for dealing with int values.

public void Transfer(ref int value)
{
    if (Loading)
        value = ReadInt();
    else
        WriteInt(value);
}

There would be similar methods for other primitive types - bool, string, etc.

Then on a class that needs to be "transferable", you would write a method like this:

public void TransferViaStorage(Storage s)
{
    s.Transfer(ref _firstName);
    s.Transfer(ref _lastName);
    s.Transfer(ref _salary);
}

This same single method can either load the fields from the Storage, or save the fields to the Storage, depending what mode the Storage object is in.

Really you're just listing all the fields that need to be transferred, so it closely approaches declarative programming instead of imperative. This means that you don't need to write two functions (one for reading, one for writing) and given that the design I'm using here is order-dependent then it's very handy to know for sure that the fields will always be read/written in identical order.

The general point is that when a parameter is marked as ref, you don't know whether the method is going to read it or write to it, and this allows you to design visitor classes that work in one of two directions, intended to be called in a symmetrical way (i.e. with the visited method not needing to know which direction-mode the visitor class is operating in).

Comparison: Attributes + Reflection

Why do this instead of attributing the fields and using reflection to automatically implement the equivalent of TransferViaStorage? Because sometimes reflection is slow enough to be a bottleneck (but always profile to be sure of this - it's hardly ever true, and attributes are much closer to the ideal of declarative programming).

The real use for this is when you create a struct. Structs in C# are value types and therefore always are copied completely when passed by value. If you need to pass it by reference, for example for performance reasons or because the function needs to make changes to the variable, you would use the ref keyword.

I could see if someone has a struct with 100 values (obviously a problem already), you'd likely want to pass it by reference to prevent 100 values copying. That and returning that large struct and writing over the old value would likely have performance issues.

The obvious reason for using the "ref" keyword is when you want to pass a variable by reference. For example passing a value type like System.Int32 to a method and alter it's actual value. A more specific use might be when you want to swap two variables.

public void Swap(ref int a, ref int b)
{
   ...
}

The main reason for using the "out" keyword is to return multiple values from a method. Personally I prefer to wrap the values in a specialized struct or class since using the out parameter produces rather ugly code. Parameters passed with "out" - is just like "ref" - passed by reference.

public void DoMagic(out int a, out int b, out int c, out int d)
{
   ...
}

There is one clear case when you must use the 'ref' keyword. If the object is defined but not created outside the scope of the method that you intend to call AND the method you want to call is supposed to do the 'new' to create it, you must use 'ref'. e.g.{object a; Funct(a);} {Funct(object o) {o = new object; o.name = "dummy";} will NOT do a thing with object 'a' nor will it complain about it at either compile or run time. It just won't do anything. {object a; Funct(ref a);} {Funct(object ref o) {o = new object(); o.name = "dummy";} will result in 'a' being a new object with the name of "dummy". But if the 'new' was already done, then ref not needed (but works if supplied). {object a = new object(); Funct(a);} {Funct(object o) {o.name = "dummy";}

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