매개 변수가 일정 할 수 있습니까?


83

Java와 동등한 C #을 찾고 final있습니다. 존재합니까?

C #에는 다음과 같은 것이 있습니까?

public Foo(final int bar);

위의 예 bar에서은 읽기 전용 변수이며로 변경할 수 없습니다 Foo(). C #에서이 작업을 수행하는 방법이 있습니까?

예를 들어, 어쩌면 내가 함께 작업 할 긴 방법이 x, y그리고 z일부 개체 (INT)로 좌표를. 함수가 어떤 식 으로든 이러한 값을 변경하지 않아 데이터가 손상되지 않는다는 것을 절대적으로 확신하고 싶습니다. 따라서 읽기 전용으로 선언하고 싶습니다.

public Foo(int x, int y, int z) {
     // do stuff
     x++; // oops. This corrupts the data. Can this be caught at compile time?
     // do more stuff, assuming x is still the original value.
}

stackoverflow.com/questions/2125591/의 중복… (그리고 Eric Lippert, btw의 훌륭한 답변이 있음)
casperOne

12
나는 그것의 정확한 중복을 생각하지 않는다. 이 질문은 참조에 의한 전달과 값에 의한 전달의 차이에 관한 것입니다. 나는 Rosarch가 그가 지나치려는 지점에 대해 나쁜 예제 코드를 사용했을 것이라고 생각합니다.
Corey Sunwold

@Rosarch : "최종"이라는 단어에서 이해하고있는 것은 무엇이든간에 개체에 대해 더 이상 작업을 수행 할 수 없다는 것입니다. 클래스에 적용된 "최종"은 C #에서 "봉인"과 동등하다는 것을 이해합니다. 그러나 어쨌든이 키워드 "최종"의 이점 또는 실제 사용은 무엇입니까? 언젠가는 JAVA 소스 코드에서 거의 모든 곳에서 본 적이 있습니다.
Will Marcouiller

3
@Will Marcouiller final 키워드를 사용하면 변경할 수없는 개체가 아닙니다. 개체의 내부에서 변경되는 메서드를 사용할 수 있기 때문에 변경할 수없는 개체에 대한 참조입니다. 주어진 예제와 같이 값에 의한 전달 상황의 경우 값이 변경되므로 x ++가 유효한 연산이되지 못합니다. 장점은 컴파일 시간 온 전성 검사를 통해 값을 다르게 설정하지 않는지 확인하는 것입니다. 그러나 내 경험상 그런 기능이 필요하지 않았습니다.
Corey Sunwold

+1 @Corey Sunwold :이 정확성에 감사드립니다. 지금은 C #과 JAVA의 유사점 만 알고 있습니다. C #의 개념은 JAVA에서 "상속"된 것입니다. 이것은 JAVA가 전 세계적으로 널리 퍼져 있다는 것을 알고 있기 때문에 JAVA에 대해 더 많이 알 수 있도록 격려합니다.
Will Marcouiller

답변:


60

불행히도 C #에서는 이것을 할 수 없습니다.

const키워드는 지역 변수 및 필드에 사용할 수 있습니다.

readonly키워드는 필드에 사용할 수 있습니다.

참고 : Java 언어는 메소드에 대한 최종 매개 변수도 지원합니다. 이 기능은 C #에 존재하지 않습니다.

에서 http://www.25hoursaday.com/CsharpVsJava.html

편집 (2019/08/13) : 이것이 허용되고 목록에서 가장 높기 때문에 가시성을 위해 이것을 던졌습니다. 이제 in매개 변수 로 가능합니다 . 자세한 내용 아래 답변을 참조하십시오.


1
"운수 나쁘게"? 현재 기능과 어떻게 다른가요?
John Saunders

31
@John Saunders 안타깝게도 Rosarch는 존재하지 않는 기능을 찾고 있기 때문입니다.
Corey Sunwold

7
@John : 메서드 내에서 일정하지 않습니다. 그게 문제입니다.
Noon Silk

6
이 메서드의 범위를 벗어난 유일한 상수입니다. 참조 또는 값으로 전달되었는지 여부를 무시하고 Rosarch는 메서드의 범위 내에서 매개 변수가 변경되는 것을 원하지 않습니다. 그것이 주요 차이점입니다.
Corey Sunwold

5
@silky : 그의 게시물을 읽는 것은 그가 요구하는 것이 아닙니다. 그는 메서드가 실제 매개 변수를 변경하지 않도록 요청하고 있습니다.
John Saunders

30

이제 C # 버전 7.2에서 가능합니다.

in메소드 서명에 키워드를 사용할 수 있습니다 . MSDN 설명서 .

in키워드는 메소드의 인수를 지정하기 전에 추가해야합니다.

예, C # 7.2의 유효한 메서드 :

public long Add(in long x, in long y)
{
    return x + y;
}

다음은 허용되지 않습니다.

public long Add(in long x, in long y)
{
    x = 10; // It is not allowed to modify an in-argument.
    return x + y;
}

수정을 시도 x하거나 다음 yin같이 표시되어 있기 때문에 다음 오류 가 표시됩니다 .

읽기 전용 변수이므로 'in long'변수에 할당 할 수 없습니다.

in의미 로 인수 표시 :

이 메소드는이 매개 변수로 사용되는 인수의 값을 수정하지 않습니다.


3
물론 참조 유형에는 유용하지 않습니다. in이러한 유형의 매개 변수에 키워드를 사용 하는 경우에만 할당을 방지하십시오. 액세스 가능한 필드 또는 속성 수정을 막지 마십시오! 내가 맞아?
ABS

5
클래스가 아닌 구조체 / 값에서만 잘 작동한다는 점에 유의하십시오. 참조에 의해 인스턴스를 수정할 수 있으므로 훨씬 나쁩니다. 그리고 어떤 경고도받지 않습니다. 주의해서 사용하십시오. blog.dunnhq.com/index.php/2017/12/15/…
jeromej

8

여기에 많은 반대표를 얻을 수있는 짧고 달콤한 답변이 있습니다. 게시물과 댓글을 모두 읽지 않았으므로 이전에 제안 된 적이 있다면 용서해주십시오.

매개 변수를 불변으로 노출하는 객체에 전달한 다음 메서드에서 해당 객체를 사용하는 것이 어떻습니까?

나는 이것이 이미 고려 된 매우 명백한 해결 방법이라는 것을 알고 있으며 OP는이 질문을함으로써 이것을 피하려고 노력하고 있지만 그럼에도 불구하고 여기에 있어야한다고 느꼈습니다 ...

행운을 빕니다 :-)


1
회원은 여전히 ​​수정할 수 있기 때문입니다. 이 C ++ 코드는 다음을 보여줍니다 int* p; *p = 0;.. 세분화 오류가 발생할 때까지 컴파일되고 실행됩니다.
콜 존슨

나는 문제에 대한 해결책이 아니기 때문에 투표했습니다. 함수 헤드에 인수를 저장하고 마지막에 비교하고 변경시 예외를 throw 할 수도 있습니다. :) 지금까지 최악의 솔루션 경연 대회가 있다면 나는 다시 주머니에 그것을 유지하는거야
릭 오셔

8

대답 : C #에는 C ++와 같은 const 기능이 없습니다.

나는 Bennett Dill에 동의합니다.

const 키워드는 매우 유용합니다. 예제에서 당신은 int를 사용했고 사람들은 당신의 요점을 얻지 못했습니다. 그러나 매개 변수가 해당 함수 내에서 변경할 수없는 사용자 거대하고 복잡한 개체 인 경우 왜? 그것은 const 키워드의 사용입니다 : 매개 변수는 그 메소드에서 중요하지 않기 때문에 그 메소드 안에서 변경할 수 없습니다. Const 키워드는 매우 강력하며 C #에서는 정말 그리워요.


7

int부분 부터 시작하겠습니다 . int값 유형이며 .Net에서는 실제로 사본을 처리하고 있음을 의미합니다. "이 값의 복사본을 가질 수 있습니다. 이것은 내 복사본이 아니라 귀하의 복사본입니다. 다시는 볼 수 없습니다. 그러나 복사본을 변경할 수는 없습니다." 이 값을 복사해도 괜찮다는 것은 메서드 호출에서 암시 적입니다. 그렇지 않으면 메서드를 안전하게 호출 할 수 없습니다. 메서드에 원본이 필요한 경우 구현 자에게 맡기고 복사본을 만들어 저장합니다. 메소드에 값을 제공하거나 메소드에 값을 제공하지 마십시오. 그 사이에 엉망진창을하지 마십시오.

참조 유형으로 이동하겠습니다. 이제 약간 혼란스러워집니다. 참조 자체를 변경할 수없는 상수 참조 또는 완전히 잠기고 변경할 수없는 객체를 의미합니까? 전자의 경우 기본적으로 .Net의 참조가 값으로 전달됩니다. 즉, 참조 사본을 얻습니다. 따라서 우리는 본질적으로 값 유형과 동일한 상황을 가지고 있습니다. 구현자가 원본 참조를 필요로하는 경우 자체적으로 유지할 수 있습니다.

그것은 우리에게 상수 (잠금 / 불변) 객체를 남깁니다. 이것은 런타임 관점에서 괜찮아 보일 수 있지만 컴파일러가이를 어떻게 적용합니까? 속성과 메서드 모두 부작용이있을 수 있으므로 기본적으로 읽기 전용 필드 액세스로 제한됩니다. 그러한 개체는 그다지 흥미롭지 않을 것입니다.


1
나는 질문을 오해 한 것에 대해 당신을 비추천했습니다. 호출 후 값을 변경하는 것이 아니라 그 안에서 값을 변경하는 것입니다.
Noon Silk

2
@silky-질문을 오해하지 않았습니다. 함수 내에서도 이야기하고 있습니다. 함수에 보내는 것이 이상한 제한이라고 말하고 있습니다. 왜냐하면 실제로는 아무것도 멈추지 않기 때문입니다. 누군가 변경된 원래 매개 변수를 잊어 버리면 변경된 사본도 잊어 버릴 가능성이 큽니다.
Joel Coehoorn

1
동의하지 않습니다. 단순히 반대표를 설명하는 댓글 만 제공합니다.
Noon Silk

DateTime에는 읽기 전용 필드 액세스 만 있지만 여전히 흥미 롭습니다. 새 인스턴스를 반환하는 많은 메서드가 있습니다. 많은 기능적 언어는 부작용이있는 메서드를 피하고 불변 유형을 선호합니다.
Devin Garner 2013

DateTime은 참조 유형이 아니라 여전히 값 유형입니다.
Joel Coehoorn

5

읽기 전용 속성 접근 자만있는 클래스의 인터페이스를 만듭니다. 그런 다음 매개 변수가 클래스 자체가 아닌 해당 인터페이스가되도록하십시오. 예:

public interface IExample
{
    int ReadonlyValue { get; }
}

public class Example : IExample
{
    public int Value { get; set; }
    public int ReadonlyValue { get { return this.Value; } }
}


public void Foo(IExample example)
{
    // Now only has access to the get accessors for the properties
}

구조체의 경우 일반 const 래퍼를 만듭니다.

public struct Const<T>
{
    public T Value { get; private set; }

    public Const(T value)
    {
        this.Value = value;
    }
}

public Foo(Const<float> X, Const<float> Y, Const<float> Z)
{
// Can only read these values
}

그래도 주목할 가치가있는 것은 당신이 구조체와 관련하여하려는 일을하고 싶은 것이 이상하다는 것입니다. 그 메서드의 작성자는 그 메서드에서 무슨 일이 일어나고 있는지를 예상해야합니다. 메서드 내에서 값을 수정하기 위해 전달 된 값에 영향을주지 않으므로 작성중인 메서드에서 자신이 행동하는지 확인하는 것이 유일한 관심사입니다. const 및 기타 규칙을 시행하는 것보다 경계와 깨끗한 코드가 핵심이되는 시점이 있습니다.


그것은 실제로 꽤 영리합니다. 또한 동시에 여러 인터페이스를 상속 할 수 있지만 하나의 클래스 만 상속 할 수 있습니다.
Natalie Adams

이것은 도움이되며 같은 방식으로 c #에서 누락 된 성가심을 덜어줍니다. 감사합니다
Jimmyt1988

3

이런 문제가 자주 발생하는 경우 "헝가리 앱"을 고려해야합니다. 나쁜 종류 와 반대로 좋은 종류 . 이것은 일반적으로 메소드 매개 변수의 상수 성을 표현하려고 시도하지는 않지만 (너무 이례적 임) 식별자 이름 앞에 추가 "c"를 추가하는 것을 막을 수있는 것은 없습니다.

지금 반대 투표 버튼을 누르려는 모든 분들께 다음 주제에 대한 유명 인사들의 의견을 읽어보십시오.


이를 적용하기 위해 명명 규칙이 필요하지 않습니다. 사실 이후에 항상 분석 도구를 사용할 수 있습니다 (좋지는 않지만 그럼에도 불구하고). Gendarme ( mono-project.com/Gendarme )에 규칙이 있고 아마도 StyleCop / FxCop도 있다고 생각 합니다.
Noon Silk

솔루션에서 쉽게 최악의 시도 (실패). 이제 매개 변수는 쓰기 가능할뿐만 아니라 변경되지 않았다고 거짓말을함으로써 실패에 대비하고 있습니다.
릭 오셔

지금까지 두 명의 아픈 SO 사용자는 더 나쁠 수 있습니다.
Hans Passant 2018

2

나는 이것이 조금 늦을 수도 있다는 것을 압니다. 그러나 아직 다른 방법을 찾고있는 사람들에게는 C # 표준의 이러한 제한을 우회하는 다른 방법이있을 수 있습니다. 래퍼 클래스 ReadOnly <T>를 작성할 수 있습니다. 여기서 T : struct입니다. 기본 형식 T 로의 암시 적 변환을 사용합니다. 그러나 wrapper <T> 클래스로의 명시 적 변환 만 사용합니다. 개발자가 ReadOnly <T> 유형의 값으로 암시 적 설정을 시도하면 컴파일러 오류를 적용합니다. 아래에서 두 가지 가능한 용도를 보여 드리겠습니다.

USAGE 1은 변경하려면 발신자 정의가 필요합니다. 이 사용법은 "TestCalled"함수 코드의 정확성 테스트에만 사용됩니다. 릴리스 수준 / 빌드 중에는 사용하지 않아야합니다. 대규모 수학 연산에서는 변환이 과도 해지고 코드가 느려질 수 있기 때문입니다. 나는 그것을 사용하지 않을 것이지만 데모 목적으로 만 게시했습니다.

내가 제안하는 USAGE 2는 TestCalled2 함수에서 디버그 대 릴리스 사용을 보여줍니다. 또한이 접근 방식을 사용할 때 TestCaller 함수에 변환이 없지만 컴파일러 컨디셔닝을 사용하여 TestCaller2 정의를 약간 더 코딩해야합니다. 디버그 구성에서 컴파일러 오류를 확인할 수 있지만 릴리스 구성에서는 TestCalled2 함수의 모든 코드가 성공적으로 컴파일됩니다.

using System;
using System.Collections.Generic;

public class ReadOnly<VT>
  where VT : struct
{
  private VT value;
  public ReadOnly(VT value)
  {
    this.value = value;
  }
  public static implicit operator VT(ReadOnly<VT> rvalue)
  {
    return rvalue.value;
  }
  public static explicit operator ReadOnly<VT>(VT rvalue)
  {
    return new ReadOnly<VT>(rvalue);
  }
}

public static class TestFunctionArguments
{
  static void TestCall()
  {
    long a = 0;

    // CALL USAGE 1.
    // explicite cast must exist in call to this function
    // and clearly states it will be readonly inside TestCalled function.
    TestCalled(a);                  // invalid call, we must explicit cast to ReadOnly<T>
    TestCalled((ReadOnly<long>)a);  // explicit cast to ReadOnly<T>

    // CALL USAGE 2.
    // Debug vs Release call has no difference - no compiler errors
    TestCalled2(a);

  }

  // ARG USAGE 1.
  static void TestCalled(ReadOnly<long> a)
  {
    // invalid operations, compiler errors
    a = 10L;
    a += 2L;
    a -= 2L;
    a *= 2L;
    a /= 2L;
    a++;
    a--;
    // valid operations
    long l;
    l = a + 2;
    l = a - 2;
    l = a * 2;
    l = a / 2;
    l = a ^ 2;
    l = a | 2;
    l = a & 2;
    l = a << 2;
    l = a >> 2;
    l = ~a;
  }


  // ARG USAGE 2.
#if DEBUG
  static void TestCalled2(long a2_writable)
  {
    ReadOnly<long> a = new ReadOnly<long>(a2_writable);
#else
  static void TestCalled2(long a)
  {
#endif
    // invalid operations
    // compiler will have errors in debug configuration
    // compiler will compile in release
    a = 10L;
    a += 2L;
    a -= 2L;
    a *= 2L;
    a /= 2L;
    a++;
    a--;
    // valid operations
    // compiler will compile in both, debug and release configurations
    long l;
    l = a + 2;
    l = a - 2;
    l = a * 2;
    l = a / 2;
    l = a ^ 2;
    l = a | 2;
    l = a & 2;
    l = a << 2;
    l = a >> 2;
    l = ~a;
  }

}

ReadOnly가 struct이고 VT가 struct와 class가 될 수 있다면 더 좋을 것입니다. 그러면 VT가 struct이면 값이 값으로 전달되고 클래스이면 값이 참조로 전달되고 원하는 경우 값이 전달됩니다. Java의 마지막 연산자 ReadOnly는 암시 적이어야합니다.
Tomer Wolberg

0

구조체가 메서드에 전달되면 ref에 의해 전달되지 않는 한 전달 된 메서드에 의해 변경되지 않습니다. 그래서 그런 의미에서 그렇습니다.

메서드 내에서 값을 할당 할 수 없거나 메서드 내에서 속성을 설정할 수없는 매개 변수를 만들 수 있습니까? 아니요. 메서드 내에서 값이 할당되는 것을 방지 할 수는 없지만 변경 불가능한 유형을 만들어 속성이 설정되는 것을 방지 할 수 있습니다.

문제는 매개 변수 또는 속성이 메서드 내에서 할당 될 수 있는지 여부가 아닙니다. 문제는 메소드가 종료 될 때 무엇이 ​​될 것인지입니다.

외부 데이터가 변경되는 유일한 경우는 클래스를 전달하고 해당 속성 중 하나를 변경하거나 ref 키워드를 사용하여 값을 전달하는 경우입니다. 당신이 설명한 상황은 어느 쪽도 아닙니다.


불변성에 대한 의견을 제외하고는 +1 할 것입니다. 불변 유형을 갖는 것은 그가 찾고있는 것을 달성하지 못할 것입니다.
Adam Robinson

물론 기본적으로 그렇습니다. 참조로 전달되지 않는 변경 불가능한 유형은 메서드 외부의 값이 변경되지 않도록합니다.
David Morton

1
사실이지만 그의 예는 메서드 내부 의 값을 변경 하는 것입니다. 그의 예제 코드에서 x는 변경 불가능한 Int32이지만 여전히 x++. 즉, 그는 매개 변수 값의 가변성과 직교하는 매개 변수의 재 할당을 방지하려고합니다.
itowlson

1
@silky Kinda는 같은 질문을 오해 한 세 가지 답변에 반대 투표를하는 것이 이상합니다. 아마도 질문이 오해 된 것은 독자의 잘못이 아닐 것입니다. 남자가 세 번째 아내와 이혼하면 일반적으로 아내의 잘못이 아닙니다.
David Morton

2
@David : 투표 시스템의 목적입니다. 관련성에 따라 주문합니다. 시정해야하는 이유가 무엇인지 모르겠습니다.
Noon Silk
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.