development

생성자 또는 선언시 클래스 필드를 초기화 하시겠습니까?

big-blog 2020. 2. 23. 11:56
반응형

생성자 또는 선언시 클래스 필드를 초기화 하시겠습니까?


나는 최근에 C #과 Java로 프로그래밍 해 왔으며 클래스 필드를 초기화하는 가장 좋은 곳이 궁금합니다.

선언 할 때해야합니까? :

public class Dice
{
    private int topFace = 1;
    private Random myRand = new Random();

    public void Roll()
    {
       // ......
    }
}

또는 생성자에서? :

public class Dice
{
    private int topFace;
    private Random myRand;

    public Dice()
    {
        topFace = 1;
        myRand = new Random();
    }

    public void Roll()
    {
        // .....
    }
}

참전 용사 중 일부가 최선의 방법이라고 생각합니다. 일관성 있고 하나의 접근 방식을 고수하고 싶습니다.


내 규칙:

  1. 선언의 기본 값으로 초기화하지 마십시오 ( null, false, 0, 0.0...).
  2. 필드 값을 변경하는 생성자 매개 변수가없는 경우 선언에서 초기화를 선호하십시오.
  3. 생성자 매개 변수로 인해 필드 값이 변경되면 생성자에 초기화를 두십시오.
  4. 연습에서 일관성을 유지하십시오 (가장 중요한 규칙).

C #에서는 중요하지 않습니다. 당신이 제공하는 두 개의 코드 샘플은 완전히 동일합니다. 첫 번째 예에서 C # 컴파일러 (또는 CLR?)는 빈 생성자를 생성하고 마치 마치 생성자에있는 것처럼 변수를 초기화합니다 (Jon Skeet이 아래 주석에서 설명하는 것과 약간의 차이가 있습니다). 이미 생성자가 있으면 "위"의 초기화가 맨 위로 이동합니다.

모범 사례의 관점에서 전자는 다른 생성자를 쉽게 추가하고 연결하는 것을 잊어 버릴 수 있기 때문에 전자가 후자보다 오류가 적습니다.


C #의 의미는 여기서 Java와 약간 다릅니다. C #에서는 선언에서 선언이 수퍼 클래스 생성자를 호출하기 전에 수행됩니다. Java에서는 'this'를 사용할 수있게 된 직후에 수행되며 (익명 내부 클래스에 특히 유용함) 두 형식의 의미가 실제로 일치 함을 의미합니다.

가능하면 필드를 최종적으로 만드십시오.


하나의 경고가 있다고 생각합니다. 한 번 그런 오류를 저지른 적이 있습니다. 파생 클래스 내부에서 추상 기본 클래스에서 상속 된 필드를 "선언시 초기화"하려고했습니다. 그 결과 두 개의 필드 세트가 존재했습니다. 하나는 "기본"이고 다른 하나는 새로 선언 된 것이므로 디버그하는 데 시간이 꽤 걸립니다.

교훈 : 상속 된 필드 를 초기화 하려면 생성자 내에서 수행하십시오.


예제에서 유형을 가정하면 생성자에서 필드를 초기화하는 것이 좋습니다. 예외적 인 경우는 다음과 같습니다.

  • 정적 클래스 / 메소드의 필드
  • 정적 / 최종 / et al로 입력 된 필드

나는 항상 클래스 상단의 필드 목록을 목차 (사용 방법이 아니라 여기에 포함 된 내용)로, 생성자를 소개로 생각합니다. 물론 방법은 장입니다.


내가 말하면 어떻게됩니까?

나는 일반적으로 모든 것을 초기화하고 일관된 방식으로 수행합니다. 예, 지나치게 명시 적이지만 유지 관리가 조금 더 쉽습니다.

우리가 성능에 대해 걱정한다면 잘해야 할 일만 초기화하고 가장 많은 돈을 벌 수있는 영역에 배치하십시오.

실시간 시스템에서는 변수 또는 상수가 필요한지 의문입니다.

그리고 C ++에서는 종종 어느 곳에서나 초기화하지 않고 Init () 함수로 옮깁니다. 왜? 글쎄, C ++에서 객체 생성 중에 예외를 던질 수있는 것을 초기화하면 메모리 누수가 발생합니다.


많은 상황이 있습니다.

빈 목록 만 있으면됩니다

상황은 분명하다. 내 목록을 준비하고 누군가가 목록에 항목을 추가 할 때 예외가 발생하지 않도록해야합니다.

public class CsvFile
{
    private List<CsvRow> lines = new List<CsvRow>();

    public CsvFile()
    {
    }
}

나는 가치를 안다

나는 기본적으로 어떤 값을 갖고 싶어하는지 알고 있거나 다른 논리를 사용해야합니다.

public class AdminTeam
{
    private List<string> usernames;

    public AdminTeam()
    {
         usernames = new List<string>() {"usernameA", "usernameB"};
    }
}

또는

public class AdminTeam
{
    private List<string> usernames;

    public AdminTeam()
    {
         usernames = GetDefaultUsers(2);
    }
}

가능한 값이있는 빈 목록

때로는 다른 생성자를 통해 값을 추가 할 수있는 빈 목록이 기본적으로 필요합니다.

public class AdminTeam
{
    private List<string> usernames = new List<string>();

    public AdminTeam()
    {
    }

    public AdminTeam(List<string> admins)
    {
         admins.ForEach(x => usernames.Add(x));
    }
}

Java에서 선언이있는 이니셜 라이저는 생성자가 사용되는 생성자 (둘 이상이있는 경우) 또는 생성자의 매개 변수 (인수가있는 경우)에 관계없이 필드가 항상 동일한 방식으로 초기화됨을 의미합니다. 값을 변경하십시오 (최종이 아닌 경우). 따라서 선언자와 함께 이니셜 라이저를 사용하면 초기화 된 값 이 사용 된 생성자와 생성자에 전달 된 매개 변수에 관계없이 모든 경우에 필드가 갖는 값임을 독자에게 제안합니다 . 따라서 생성 된 모든 객체의 값이 동일한 경우에만 선언과 함께 이니셜 라이저를 사용하십시오.


C #의 디자인은 인라인 초기화가 선호되거나 언어에 있지 않을 것을 제안합니다. 코드의 다른 위치 사이의 상호 참조를 피할 수 있으면 일반적으로 더 좋습니다.

정적 필드 초기화와의 일관성 문제도 있으며, 이는 최상의 성능을 위해 인라인이어야합니다. 생성자 설계를 위한 프레임 워크 설계 지침 은 다음과 같이 말합니다.

✓ 런타임은 명시 적으로 정의 된 정적 생성자가없는 유형의 성능을 최적화 할 수 있기 때문에 정적 생성자를 명시 적으로 사용하지 않고 정적 필드를 초기화하는 것을 고려하십시오.

이와 관련하여 "고려"란 정당한 이유가없는 한 그렇게하는 것을 의미합니다. 정적 이니셜 라이저 필드의 경우 초기화가 너무 복잡하여 인라인으로 코딩 할 수없는 것이 좋습니다.


일관성을 유지하는 것이 중요하지만 이것이 바로 다른 질문에 대한 생성자가 있습니까?라는 질문입니다.

일반적으로 클래스 자체가 변수의 하우징으로 작동하는 것 외에는 아무것도하지 않는 데이터 전송 모델을 만들고 있습니다.

이 시나리오에서는 일반적으로 메서드 나 생성자가 없습니다. 목록을 초기화하는 독점적 인 목적을 위해 생성자를 만드는 것이 어리석은 일입니다. 특히 선언과 함께 목록을 초기화 할 수 있기 때문입니다.

다른 많은 사람들이 말했듯이, 그것은 당신의 사용법에 달려 있습니다. 간단하게 유지하고 불필요한 것을 추가하지 마십시오.


생성자가 둘 이상인 상황을 고려하십시오. 생성자마다 초기화가 다릅니 까? 그것들이 동일하다면 왜 각 생성자에 대해 반복합니까? 이것은 kokos 문과 일치하지만 매개 변수와 관련이 없을 수 있습니다. 예를 들어, 객체가 어떻게 생성되었는지 보여주는 플래그를 유지하려고한다고 가정 해 봅시다. 그런 다음 생성자 매개 변수에 관계없이 다른 생성자에 대해 해당 플래그가 다르게 초기화됩니다. 반면에, 각 생성자에 대해 동일한 초기화를 반복하면 일부 생성자에서 의도하지 않게 초기화 매개 변수를 변경할 수 있지만 다른 구성자에서는 초기화 매개 변수가 변경 될 가능성이 있습니다. 따라서 기본 개념은 공통 코드는 공통 위치를 가져야하며 다른 위치에서 잠재적으로 반복되지 않아야한다는 것입니다.


선언에서 값을 설정하면 약간의 성능 이점이 있습니다. 생성자에서 설정하면 실제로 두 번 설정됩니다 (먼저 기본값으로 설정 한 다음 ctor에서 재설정).


나는 일반적으로 생성자를 시도하여 의존성을 얻고 관련 인스턴스 멤버를 초기화하는 것 외에는 아무것도하지 않습니다. 이것은 당신이 당신의 수업을 단위 테스트하려는 경우 인생을 더 쉽게 만들 것입니다.

인스턴스 변수에 할당 할 값이 생성자에게 전달할 매개 변수의 영향을받지 않으면 선언시 값을 할당하십시오.


모범 사례 에 대한 귀하의 질문에 대한 직접적인 대답은 아니지만 중요하고 관련된 새로 고침 요점은 일반 클래스 정의의 경우 기본값으로 초기화하기 위해 컴파일러에 그대로 두거나 필드를 초기화하기 위해 특별한 방법을 사용해야한다는 것입니다 기본값으로 (코드 가독성에 절대적으로 필요한 경우).

class MyGeneric<T>
{
    T data;
    //T data = ""; // <-- ERROR
    //T data = 0; // <-- ERROR
    //T data = null; // <-- ERROR        

    public MyGeneric()
    {
        // All of the above errors would be errors here in constructor as well
    }
}

그리고 일반 필드를 기본값으로 초기화하는 특별한 방법은 다음과 같습니다.

class MyGeneric<T>
{
    T data = default(T);

    public MyGeneric()
    {           
        // The same method can be used here in constructor
    }
}

참고 URL : https://stackoverflow.com/questions/24551/initialize-class-fields-in-constructor-or-at-declaration



반응형