전용 정적 메소드 사용의 장점


209

일반적으로 코드 중복을 줄이기 위해 내부 전용 메서드가있는 클래스를 만들 때 인스턴스 필드를 사용할 필요가없는 경우 메서드를 정적으로 선언하면 성능이나 메모리 이점이 있습니까?

예:

foreach (XmlElement element in xmlDoc.DocumentElement.SelectNodes("sample"))
{
    string first = GetInnerXml(element, ".//first");
    string second = GetInnerXml(element, ".//second");
    string third = GetInnerXml(element, ".//third");
}

...

private static string GetInnerXml(XmlElement element, string nodeName)
{
    return GetInnerXml(element, nodeName, null);
}

private static string GetInnerXml(XmlElement element, string nodeName, string defaultValue)
{
    XmlNode node = element.SelectSingleNode(nodeName);
    return node == null ? defaultValue : node.InnerXml;
}

GetInnerXml () 메서드를 정적으로 선언하면 어떤 이점이 있습니까? 의견이 없습니다. 의견이 있습니다.


1
가능한 가능한 방법의
— drzaus

답변:


221

로부터 의 FxCop 규칙 페이지 이것에 :

메소드를 정적으로 표시 한 후 컴파일러는 비가 상 호출 사이트를 해당 멤버에게 내 보냅니다. 비가 상 호출 사이트를 내 보내면 각 호출에 대해 런타임시 현재 개체 포인터가 널이 아닌지 확인하지 못합니다. 이로 인해 성능에 민감한 코드의 성능이 크게 향상 될 수 있습니다. 경우에 따라 현재 객체 인스턴스에 액세스하지 못하면 정확성 문제가 발생합니다.


37
또한 "정적"절은 해를 끼치 지 않으며 이미 한 단어로 "문서"를 제공한다고 덧붙였습니다. 이 메소드는 인스턴스 멤버를 사용하지 않고
— 있으며이

20
"메소드가 상태 액세스를 필요로하지 않는다면 (이것은 정적 인 상태로 만드십시오").
— DanMan

3
균형을 위해 많은 사람들이 다형성을 깨뜨리고 테스트하기 위해 객체를 스터 빙 할 수 없기 때문에 일반적으로 정적 메소드에 반대한다는 점을 지적 할 가치가 있습니다. 예를 들어 googletesting.blogspot.co.uk/2008/12/…
— Andy

@ 앤디-좋은 지적. 선을 그리는 한 가지 방법은 정적 메서드가 전달하는 매개 변수 외부의 항목에 액세스하는지 여부를 보는 것입니다. 이러한 방식으로 자체 포함되어 있으면 테스트하기 쉬워야하며 필요가 없습니다. 아무것도 스터브합니다.
— Neil

4
많은 개발자들이 "private static"에 익숙하지 않습니다. 팀의 공통 코드 기반에서 사용했으며 혼란을 겪었습니다. 그 대가로, 그것은 아주 작은 이점을 제공합니다. 우리는 코드의 의미에 대해 코드를 유지 관리하는 모든 미래 개발자를 포함하여 팀의 모든 사람을 교육하도록 선택할 수 있습니다. 그러나 프라이빗 메소드를 프라이빗 정적으로 전환하는 이점은 매우 적으므로 (즉, 인스턴스 데이터에 대한 의존성을 제거) 노력과 혼란을 겪을 가치가 없습니다. 이 방법은 이미 개인 범위입니다. 실제로 알 필요가없는 언어 문제입니다.
— 커티스 얄롭

93

클래스를 작성할 때 대부분의 메소드는 두 가지 범주로 나뉩니다.

  • 현재 인스턴스의 상태를 사용 / 변경하는 메소드
  • 현재 객체의 상태를 사용 / 변경하지 않지만 다른 곳에서 필요한 값을 계산하는 데 도움이되는 도우미 메서드.

정적 메서드는 서명을 살펴보면 현재 인스턴스의 상태를 사용하거나 수정하지 않는다는 것을 알기 때문에 유용합니다.

이 예제를 보자 :

공공 수업 도서관
{
    private static Book findBook (목록 <책> 책, 문자열 제목)
    {
        // 코드는 여기에 간다
    }
}

라이브러리 상태의 인스턴스가 망가 졌을 때 이유를 알아 내려고 노력하고 있다면 시그너처에서 findBook을 범인으로 배제 할 수 있습니다.

나는 메소드 나 함수의 서명으로 가능한 한 많이 의사 소통하려고 노력한다. 이것은 훌륭한 방법이다.


1
C ++에서 const-method 선언의 종류, 그렇지 않습니까?
— anhoppe

그렇습니다. 언어를 사용하여 잘못 될 수있는 것을 제한함으로써 사물을 단순화하는 또 다른 좋은 방법입니다.
— Neil

반드시 그런 것은 아닙니다. 의는이 있다고 가정하자 Library인스턴스 필드가 List<Book> _books(당신이 설계하는 것 아니 어떻게 책을 저장하기를 Library아마하지만 승 / E 클래스), 그것은이 목록을 통과 findBook하고, 정적 메소드 호출 books.Clear()또는 books.Reverse()등등. 정적 메소드에 변경 가능한 상태에 대한 참조에 대한 액세스 권한을 부여하면 해당 정적 메소드가 상태를 엉망으로 만들 수 있습니다.
— sara

1
진실. 이 경우 서명은이 메소드가 라이브러리 인스턴스에 액세스하고 변경하는 기능을 가지고 있음을 보여줍니다.
— Neil

우리가 사용할 수있는 거의 모든 보호 구조에는이를 약화시킬 수있는 방법이 있습니다. 그러나 그것들을 사용하는 것은 여전히 ​​현명하며 "성공의 구덩이"를 향한 올바른 방향으로 우리를 감동시키는 데 도움이됩니다.
— Neil

81

정적 메서드를 호출하면 MSIL (Microsoft Intermediate Language)로 호출 명령어가 생성되지만 인스턴스 메서드를 호출하면 callvirt 명령어가 생성되어 null 객체 참조도 확인합니다. 그러나 대부분 두 경우의 성능 차이는 중요하지 않습니다.

src : MSDN-http://msdn.microsoft.com/en-us/library/79b3xss3(v= vs.110).aspx


15

그렇습니다. 컴파일러는 암시 적 this포인터를 static메서드 에 전달할 필요가 없습니다 . 인스턴스 메소드에서 사용하지 않더라도 여전히 전달됩니다.


런타임시 성능 또는 메모리 이점과 어떤 관련이 있습니까?
— Scott Dorman

11
추가 매개 변수를 전달하면 CPU가 해당 매개 변수를 레지스터에 배치하고 인스턴스 메소드가 다른 메소드를 호출하는 경우 스택에 푸시하기 위해 추가 작업을 수행해야 함을 의미합니다.
— Kent Boogaart

5

이 매개 변수가 전달되지 않기 때문에 약간 빠릅니다 (메소드를 호출하는 데 드는 성능 비용은 아마도이 절약보다 훨씬 더 높습니다).

개인 정적 메소드에 대해 생각할 수있는 가장 좋은 이유는 실수로 객체를 변경할 수 없다는 것을 의미하기 때문입니다 (이 포인터가 없기 때문에).


4

따라서 함수가 정적으로 사용하는 클래스 범위 멤버도 선언해야한다는 점을 기억해야합니다. 이렇게하면 각 인스턴스에 대해 해당 항목을 작성하는 메모리가 절약됩니다.


그것이 클래스 범위 변수이기 때문에 정적이어야 함을 의미하지는 않습니다.
— Scott Dorman

3
이 정적 방법으로 사용되는 경우 아니, 그것은 반드시 정적. 메서드가 정적이 아닌 경우 클래스 멤버를 정적으로 만들지 않았을 수 있으므로 클래스의 각 인스턴스에 더 많은 메모리가 사용됩니다.
— Joel Coehoorn

2

나는 실제로 할 수 없다면 모든 개인 메소드가 정적 인 것을 매우 선호합니다. 나는 다음을 훨씬 선호한다.

public class MyClass
{
    private readonly MyDependency _dependency;

    public MyClass(MyDependency dependency)
    {
        _dependency = dependency;
    }

    public int CalculateHardStuff()
    {
        var intermediate = StepOne(_dependency);
        return StepTwo(intermediate);
    }

    private static int StepOne(MyDependency dependency)
    {
        return dependency.GetFirst3Primes().Sum();
    }

    private static int StepTwo(int intermediate)
    {
        return (intermediate + 5)/4;
    }
}

public class MyDependency
{
    public IEnumerable<int> GetFirst3Primes()
    {
        yield return 2;
        yield return 3;
        yield return 5;
    }
}

인스턴스 필드에 액세스하는 모든 메소드를 통해 왜 이런거야? 이 계산 과정이 더 복잡해지고 클래스가 15 개의 개인 도우미 메서드로 끝나기 때문에 의미있는 의미로 단계의 하위 집합을 캡슐화하는 새로운 클래스로 가져올 수 있기를 원합니다.

언제 MyClass 우리는 로깅도 (상투 예를 용서하시기 바랍니다) 웹 서비스를 통지 할 필요가 필요가 있기 때문에 더 종속성을 얻을, 그것은 쉽게 방법이있는 종속성이 무엇을보고 정말 도움이됩니다.

R #과 같은 도구를 사용하면 몇 번의 키 입력으로 일련의 개인 정적 메소드에서 클래스를 추출 할 수 있습니다. 모든 개인 헬퍼 메소드가 인스턴스 필드에 밀접하게 연결되어 있으면 상당히 어려울 수 있습니다.


-3

이미 언급했듯이 정적 메소드에는 많은 장점이 있습니다. 하나; 응용 프로그램의 수명 동안 힙에 살게됩니다. 최근에 Windows 서비스에서 메모리 누수를 추적하는 데 하루를 보냈습니다 ... 누설은 IDisposable을 구현하고 using 문에서 일관되게 호출 된 클래스 내부의 개인 정적 메서드로 인해 발생했습니다. 이 클래스가 작성 될 때마다 클래스 내 정적 메소드의 힙에 메모리가 예약되어 있습니다. 불행히도 클래스를 폐기 할 때 정적 메소드의 메모리가 해제되지 않았습니다. 이로 인해이 서비스의 메모리 공간이 며칠 내에 서버의 사용 가능한 메모리를 사용하여 예측 가능한 결과를 얻었습니다.


4
이것은 말이되지 않습니다. 힙은 결코 의 코드 메모리를 저장하지 어떤 방법, 정적 또는 그렇지. 힙은 객체 인스턴스 용입니다. 매개 변수, 리턴 값, 비 호이스트 로컬 등에 대한 메모리를 보유하기 위해 메소드를 호출 할 때마다 스택에 데이터가 있지만 메소드 실행이 완료되면 모든 데이터가 사라집니다.
— Servy
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.