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


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.