C #에서 정적 메서드로 인터페이스를 구현할 수없는 이유는 무엇입니까?


447

C #이 이런 식으로 설계된 이유는 무엇입니까?

내가 이해하는 바와 같이, 인터페이스는 행동을 설명하고 특정 행동이 구현되는 인터페이스를 구현하는 클래스에 대한 계약 의무를 설명하는 목적을 제공합니다.

클래스가 공유 메소드에서 해당 동작을 구현하려면 왜 안됩니까?

다음은 내가 염두에 둔 예입니다.

// These items will be displayed in a list on the screen.
public interface IListItem {
  string ScreenName();
  ...
}

public class Animal: IListItem {
    // All animals will be called "Animal".
    public static string ScreenName() {
        return "Animal";
    }
....
}

public class Person: IListItem {

    private string name;

    // All persons will be called by their individual names.
    public string ScreenName() {
        return name;
    }

    ....

 }

6
Java 8에는 그 기능이 있습니다 ( stackoverflow.com/questions/23148471/… ).
liang

1
정적 동작과 상속 또는 인터페이스 구현을 결합하는 방법을 확인하십시오. stackoverflow.com/a/13567309/880990
Olivier Jacot-Descombes

1
IListItem.ScreenName() => ScreenName()(C # 7 구문 사용) 정적 메소드를 호출하여 인터페이스 메소드를 명시 적으로 구현합니다. 상속을 추가하면 상황이 추악 해집니다 (인터페이스를 다시 구현해야 함)
Jeroen Mostert

2
기다림이 끝났다는 것을 모두에게 알리십시오! C # 8.0에는 dotnetfiddle.net/Lrzy6y와 같은 정적 인터페이스 방법이 있습니다 (OP가 원하는 방식과 약간 다르게 작동하지만 구현할 필요는 없음)
Jamie

답변:


221

왜이 작업을 수행 할 수 없는지 묻는다고 가정합니다.

public interface IFoo {
    void Bar();
}

public class Foo: IFoo {
    public static void Bar() {}
}

이것은 의미 적으로 나에게 이해가되지 않습니다. 객체와 상호 작용하기위한 계약을 지정하려면 인터페이스에 지정된 메소드가 있어야합니다. 정적 메소드를 사용하면 객체와 상호 작용할 수 없습니다. 구현이 정적 일 수있는 위치에있는 경우 해당 메소드가 실제로 인터페이스에 속하는지 스스로에게 문의해야 할 수 있습니다.


예제를 구현하기 위해 Animal에 const 속성을 지정하면 정적 컨텍스트에서 액세스 할 수 있으며 구현에서 해당 값을 반환합니다.

public class Animal: IListItem {
    /* Can be tough to come up with a different, yet meaningful name!
     * A different casing convention, like Java has, would help here.
     */
    public const string AnimalScreenName = "Animal";
    public string ScreenName(){ return AnimalScreenName; }
}

더 복잡한 상황에서는 항상 다른 정적 메서드를 선언하고 위임 할 수 있습니다. 예를 생각해 보았을 때 정적 및 인스턴스 컨텍스트에서 사소한 일을 할 이유를 생각할 수 없으므로 FooBar blob을 절약하고 표시 할 수 있습니다. 좋은 생각이 아닙니다.


6
완벽한 예! 그러나 나는 당신의 추론을 이해하지 못합니다. 분명히 컴파일러 는 statuc 멤버를 보도록 설계되었을 있습니까? 인스턴스에 theis 메소드의 구현을위한 주소 테이블이 없습니까? 이 테이블에 정적 메소드를 포함시킬 수 없습니까?
Kramii

12
이것이 유용한 경우가 있습니다. 예를 들어 모든 구현자가 XElement 인수를 사용하는 GetInstance 메서드를 구현하기를 원합니다. 인터페이스에서 정적 메소드로 정의하거나 인터페이스에서 생성자 서명을 요구할 수 없습니다.
oleks

25
"이것은 말이 안 돼요"에서 "버그에요. 당신이 할 수 있으면 좋겠어요." (유효한 사용 사례가 여기에 있다고 생각합니다.) 해결 방법으로 인스턴스 메소드는 정적 메소드에 간단히 위임 할 수 있습니다.
harpo

5
public static object MethodName(this IBaseClass base)정적 클래스 내 에서처럼 기본 인터페이스에 확장 메소드로 조각을 구현할 수도 있습니다 . 그러나 단점은 인터페이스 상속과 달리 개별 상속자가 방법론을 잘 무시하도록 강요하지 않습니다.
Troy Alford

8
제네릭에는 많은 의미가 있습니다. 예를 들어 void Something <T> () 여기서 T : ISomeInterface {new T (). DoSomething1 (); T.DoSomething2 (); }
Francisco Ryan Tolmasky I

173

필자의 기술적 인 이유는 정적 메소드가 vtable에 없으며 호출 사이트가 컴파일 타임에 선택되기 때문입니다. 재정의 또는 가상 정적 멤버를 가질 수없는 것과 같은 이유입니다. 자세한 내용을 보려면 CS 등급 또는 컴파일러 원이 필요합니다.

정치적 이유로 나는 워커 루 대학에서 수학, 컴퓨터 과학 및 응용 수학 학사 학위를 소지 한 에릭 리퍼 ( Eric Lippert)를 인용 할 것이다 (출처 : LinkedIn ) :

정적 메소드의 핵심 디자인 원칙, 그 이름을 부여하는 원칙 ... 컴파일 타임에 어떤 메소드가 호출 될지 항상 정확하게 결정할 수 있습니다. 즉,이 방법은 코드의 정적 분석에 의해서만 해결 될 수 있습니다.

Lippert는 소위 유형 방법을위한 공간을 남겨 둡니다.

즉, (정적과 같은) 유형과 관련된 메서드는 null이 불가능한 "this"인수 (인스턴스 또는 가상과는 달리)를 사용하지 않지만 호출 된 메서드는 T의 생성 된 유형 ( 컴파일 타임에 결정 해야하는 정적과는 달리).

그러나 그 유용성에 대해서는 아직 확신이 없습니다.


5
훌륭합니다. 이것은 제가 작성하고 싶었던 답변입니다. 구현 세부 사항을 알지 못했습니다.
Chris Marasti-Georg

5
좋은 대답입니다. 그리고 나는이 '유형 방법'을 원합니다! 많은 경우에 유용 할 것입니다 (유형 / 클래스의 메타 데이터에 대해 생각하십시오).
Philip Daubmeier

23
이것이 정답입니다. 누군가에게 인터페이스를 전달하면 메소드 호출 방법을 알아야합니다. 인터페이스는 단지 가상 메소드 테이블 입니다. 정적 클래스에는 없습니다. 호출자는 메소드 호출 방법 을 모릅니다 . (이 답변을 읽기 전에 C #이 단지 pedantic이라고 생각했습니다. 이제 인터페이스 무엇인지 에 의해 기술적 인 한계가 있음을 알았습니다 ). 다른 사람들이 어떻게 나쁜 디자인인지 이야기 할 것입니다. 나쁜 디자인은 아니지만 기술적 인 한계입니다.
Ian Boyd

2
실제로 질문에 대답하고 비현실적이고 꼼꼼하지 않은 +1. 이안 보이드 (Ian Boyd)가 말한 것처럼 : "나쁜 디자인이 아니라 기술적 인 한계입니다."
Joshua Pech

3
연관된 vtable을 사용하여 정적 클래스에 대한 객체를 생성 할 수 있습니다. Scala가 어떻게 처리 object하고 어떻게 인터페이스를 구현할 수 있는지 살펴보십시오 .
Sebastian Graf

97

여기서 대부분의 대답은 요점을 놓친 것 같습니다. 다형성은 인스턴스 간뿐만 아니라 유형 간에도 사용할 수 있습니다. 이것은 제네릭을 사용할 때 종종 필요합니다.

제네릭 메서드에 형식 매개 변수가 있고 해당 작업을 수행해야한다고 가정합니다. 우리는 생성자를 알지 못하기 때문에 인스턴스화하고 싶지 않습니다.

예를 들면 다음과 같습니다.

Repository GetRepository<T>()
{
  //need to call T.IsQueryable, but can't!!!
  //need to call T.RowCount
  //need to call T.DoSomeStaticMath(int param)
}

...
var r = GetRepository<Customer>()

불행히도, 나는 "못생긴"대안들만 생각 해낼 수 있습니다.

  • 리플렉션 사용 추악하고 인터페이스와 다형성의 아이디어를 능가합니다.

  • 완전히 별도의 팩토리 클래스 생성

    이것은 코드의 복잡성을 크게 증가시킬 수 있습니다. 예를 들어 도메인 객체를 모델링하려는 경우 각 객체에는 다른 리포지토리 클래스가 필요합니다.

  • 원하는 인터페이스 메소드를 인스턴스화 한 다음 호출

    일반 매개 변수로 사용되는 클래스의 소스를 제어하더라도 구현하기가 어려울 수 있습니다. 그 이유는 예를 들어 인스턴스가 잘 알려진 "DB에 연결됨"상태 일 필요가 있기 때문입니다.

예:

public class Customer 
{
  //create new customer
  public Customer(Transaction t) { ... }

  //open existing customer
  public Customer(Transaction t, int id) { ... }

  void SomeOtherMethod() 
  { 
    //do work...
  }
}

정적 인터페이스 문제를 해결하기 위해 인스턴스화를 사용하려면 다음을 수행해야합니다.

public class Customer: IDoSomeStaticMath
{
  //create new customer
  public Customer(Transaction t) { ... }

  //open existing customer
  public Customer(Transaction t, int id) { ... }

  //dummy instance
  public Customer() { IsDummy = true; }

  int DoSomeStaticMath(int a) { }

  void SomeOtherMethod() 
  { 
    if(!IsDummy) 
    {
      //do work...
    }
  }
}

이것은 분명히 추악하고 불필요한 것은 다른 모든 방법의 코드를 복잡하게 만듭니다. 분명히, 우아한 해결책도 아닙니다!


35
"대부분의 답변이 요점을 놓친 것 같습니다."에 대해 +1; 거의 모든 답변이 질문의 핵심을

5
@Chris 다음은이 제한 사항을 다시 한 번 강조한 구체적인 예입니다. IResettable 인터페이스를 클래스에 추가하여 사이트 관리자가 재설정 할 수있는 정적 변수로 특정 데이터를 캐시한다는 것을 나타냅니다 (예 : 주문 카테고리 목록, 일련의 VAT 비율, 외부 API에서 검색 된 카테고리 목록) DB 및 외부 API 적중을 줄이려면 분명히 정적 메소드 재설정을 사용합니다. 이를 통해 재설정 할 수있는 클래스 감지를 자동화 할 수 있습니다. 여전히이 작업을 수행 할 수는 있지만 방법은 IDE에 자동으로 추가되거나 추가되지 않으며 희망에 의존합니다.
mattmanser

6
@Chris 나는 동의하지 않는다. 더 많은 아키텍처가 '최상의'솔루션 인 경우 항상 언어 결함의 징후입니다. C #에서 제네릭과 익명 메서드를 얻은 이후 아무도 더 이상 이야기하지 않는 패턴을 모두 기억하십니까?
mattmanser

3
"where T : IQueryable, T : IDoSomeStaticMath"또는 이와 유사한 것을 사용할 수 없습니까?
Roger Willcocks

2
@ ChrisMarasti-Georg : 다소 더럽지 만 흥미로운 방법은이 작은 구성입니다. public abstract class DBObject<T> where T : DBObject<T>, new()그리고 모든 DB 클래스를로 상속합니다 DBObject<T>. 그런 다음 abstract 슈퍼 클래스에서 리턴 유형 T를 사용하여 키로 검색을 정적 함수로 만들고 해당 함수가 T의 새 오브젝트를 작성한 다음 String GetRetrieveByKeyQuery()해당 오브젝트에서 protected (수퍼 클래스에서 abstract로 정의 됨)를 호출하여 얻을 수 있습니다. 실행할 실제 쿼리 이 주제에 문제가 생길 수도 있지만
벗어나는 Nyerguds

20

나는 그것이 오래된 질문이라는 것을 알고 있지만 흥미 롭습니다. 예가 최고가 아닙니다. 사용 사례를 보여 주면 훨씬 더 명확 할 것이라고 생각합니다.

문자열 DoSomething <T> () 여기서 T : ISomeFunction
{
  if (T.someFunction ())
    ...
}

정적 메소드 가 인터페이스를 구현 하도록하는 것만으로는 원하는 것을 달성 할 수 없습니다. 필요한 것은 인터페이스의 일부로 정적 멤버를 갖는 것 입니다. 특히 물건을 만들 수있을 때 많은 유스 케이스를 상상할 수 있습니다. 내가 제공 할 수있는 두 가지 접근법은 도움이 될 수 있습니다.

  1. 위의 DoSomething에 전달할 형식이 type 매개 변수 인 정적 제네릭 클래스를 만듭니다. 이 클래스의 각 변형에는 해당 유형과 관련된 항목을 보유하는 하나 이상의 정적 멤버가 있습니다. 이 정보는 각 관심 클래스에 "정보 등록"루틴을 호출하거나 클래스 변형의 정적 생성자가 실행될 때 정보를 얻기 위해 Reflection을 사용하여 제공 할 수 있습니다. 후자의 접근법은 Comparer <T> .Default ()와 같은 것들에 의해 사용된다고 생각합니다.
  2. 관심있는 각 클래스 T에 대해 IGetWhateverClassInfo <T>를 구현하고 "새"제한 조건을 충족시키는 클래스 또는 구조체를 정의하십시오. 클래스에는 실제로 필드가 포함되지 않지만 유형 정보가 포함 된 정적 필드를 반환하는 정적 속성이 있습니다. 해당 클래스 또는 구조체의 유형을 해당 일반 루틴으로 전달하면 인스턴스를 작성하고이를 사용하여 다른 클래스에 대한 정보를 얻을 수 있습니다. 이 목적으로 클래스를 사용하는 경우 매번 새 디스크립터 오브젝트 인스턴스를 구성하지 않아도되도록 위에 표시된대로 정적 제네릭 클래스를 정의해야합니다. 구조체를 사용하는 경우 인스턴스화 비용은 없어야하지만 모든 구조체 유형마다 DoSomething 루틴의 다른 확장이 필요합니다.

이러한 접근법 중 어느 것도 실제로 매력적이지 않습니다. 반면에 CLR에 이러한 종류의 기능을 제공하기 위해 메커니즘이 존재한다면 .net은 매개 변수화 된 "새로운"제약 조건을 지정할 수있게 될 것입니다. 특정 서명이있는 정적 메소드가 있는지 알기가 어렵습니다.)


16

근시안적인 것 같아요.

원래 설계되었을 때 인터페이스는 클래스 인스턴스에서만 사용되도록 설계되었습니다

IMyInterface val = GetObjectImplementingIMyInterface();
val.SomeThingDefinedinInterface();

제네릭에 대한 제약 조건이 인터페이스에 정적 메서드를 추가했을 때 인터페이스가 실제로 사용 되었기 때문에 인터페이스가 도입 된 것입니다.

(댓글에 응답 :) 이제 변경하려면 CLR을 변경해야하므로 기존 어셈블리와 호환되지 않을 수 있습니다.


제네릭의 맥락에서 처음 문제가 발생했지만 인터페이스에 정적 메소드를 포함시키는 것이 다른 상황에서도 유용 할 수 있는지 궁금합니다. 사물을 바꿀 수없는 이유가 있습니까?
Kramii

매개 변수 유형을 사용하여 일부 매개 변수로 자체 클래스를 만들어야하는 일반 클래스를 구현할 때도이 문제가 발생합니다. new ()는 사용할 수 없으므로 크라 미아, 아직 어떻게해야하는지 알아 냈어?
Tom

1
@Kramii : 정적 API 계약. 예를 들어 객체 인스턴스를 원하지 않고 특정 서명을 보장합니다. IMatrixMultiplier 또는 ICustomSerializer. Funcs / Actions / Delegates 클래스 멤버가 트릭을 수행하지만 IMO는 때때로 과잉처럼 보이며 경험이없는 API를 확장하려는 시도에 혼란을 줄 수 있습니다.
David Cuccia

14

인터페이스는 객체의 동작을 지정합니다.

정적 메서드는 객체의 동작을 지정하지 않지만 어떤 방식으로 객체에 영향을주는 동작입니다.


71
미안 .. 잘 모르겠어! 인터페이스는 동작을 지정하지 않습니다. 인터페이스는 일련의 명명 된 작업을 정의합니다. 두 클래스는 완전히 다른 방식으로 동작하도록 인터페이스의 메소드를 구현할 수 있습니다. 따라서 인터페이스는 동작을 전혀 지정하지 않습니다. 그것을 구현하는 클래스.
Scott Langham

1
내가 까다 롭다고 생각하지 않기를 바랍니다. 그러나 OO를 배우는 사람이라면 누구나 이해해야 할 중요한 차이점이라고 생각합니다.
Scott Langham

4
인터페이스는 행동 및 프리젠 테이션을 포함하는 계약을 지정해야합니다. 그렇기 때문에 인터페이스 호출의 동작을 변경하는 것이 모두 해결되어야 할 이유가 아닙니다. 호출이 다르게 작동하는 인터페이스가있는 경우 (즉, IList.Add가 제거를 수행 한 경우) 올바르지 않습니다.
Jeff Yates

14
글쎄, 당신은 그 이름에 맞지 않게 행동하는 방법을 정의하기 위해 머리에 뒤 틀렸을 것입니다. 그래도 IAlertService.GetAssistance ()가있는 경우에는 표시등이 깜박이거나 경보 음이 울리거나 막대기로 눈을 찌르는 것이 동작 일 수 있습니다.
Scott Langham

1
구현은 또한 로그 파일에 쓸 수 있습니다. 이 동작은 인터페이스에 지정되어 있지 않습니다. 그러나 당신이 옳을 수도 있습니다. '지원'을받는 행동은 실제로 존중되어야합니다.
Scott Langham

14

인터페이스가 "계약"을 나타내는 한 정적 클래스가 인터페이스를 구현하는 것이 조용해 보입니다.

위의 주장은 모두 계약에 관한 요점을 놓친 것 같습니다.


2
이 간단하지만 효과적인 답변에 전적으로 동의합니다. "정적 인터페이스"에서 흥미로운 것은 계약을 나타냅니다. 어쩌면 "정적 인터페이스"라고 부르지 말아야하지만 우리는 여전히 구성을 그리워합니다. 예를 들어 ICustomMarshaler 인터페이스에 대한 .NET의 공식 문서를 확인하십시오. "문자열을 매개 변수로 허용하고 리턴 유형이 ICustomMarshaler 인 GetInstance라는 정적 메소드를 추가하려면이를 구현하는 클래스가 필요합니다. 그것은 C #에서 선호하는 반면에 평범한 영어로 "정적 인터페이스"정의처럼 심각하게 보입니다 ...
Simon Mourier

@SimonMourier이 문서는보다 명확하게 쓰여졌지만 잘못 해석하고 있습니다. GetInstance 정적 메서드가 필요한 ICustomMarshaler 인터페이스가 아닙니다. 이것을 요구하는 것은 [MarshalAs] 코드 속성입니다. 팩토리 패턴을 사용하여 속성이 첨부 된 마샬 러의 인스턴스를 가져올 수 있도록합니다. 불행히도 그들은 MarshalAs 문서 페이지에 GetInstance 요구 사항을 문서화하는 것을 완전히 잊어 버렸습니다 (내장 마샬링 구현을 사용하는 예제 만 보여줍니다).
Scott Gartner

@ScottGartner-당신의 요점을 얻지 마십시오. msdn.microsoft.com/ko-kr/library/… 명확하게 설명 : "사용자 지정 마샬 러는 ICustomMarshaler 인터페이스를 구현할뿐만 아니라 문자열을 매개 변수로 허용하고 반환 유형이 ICustomMarshaler 인 GetInstance라는 정적 메서드를 구현해야합니다. 정적 메서드는 공용 언어 런타임의 COM interop 계층에 의해 호출되어 사용자 지정 마샬 러의 인스턴스를 인스턴스화합니다. " 이것은 분명히 정적 계약 정의입니다.
Simon Mourier 2016 년

9

인터페이스의 목적은 다형성을 허용하고, 정의 된 인터페이스를 구현하기 위해 정의 된 모든 수의 정의 된 클래스의 인스턴스를 전달할 수 있기 때문에 ... 다형성 호출 내에서 코드를 찾을 수 있음 당신이 부르는 방법. 정적 메소드가 인터페이스를 구현하도록 허용하는 것은 의미가 없습니다.

어떻게 부를까요?


public interface MyInterface { void MyMethod(); }
public class MyClass: MyInterface
{
    public static void MyMethod() { //Do Something; }
}

 // inside of some other class ...  
 // How would you call the method on the interface ???
    MyClass.MyMethod();  // this calls the method normally 
                         // not through the interface...

    // This next fails you can't cast a classname to a different type... 
    // Only instances can be Cast to a different type...
    MyInterface myItf = MyClass as MyInterface;  

1
다른 언어 (예 : Java)를 사용하면 정적 인스턴스를 정적 ​​컨텍스트에서 호출해야한다는 경고가 표시되지만 객체 인스턴스에서 정적 메소드를 호출 할 수 있습니다.
Chris Marasti-Georg 18

.Net에서도 인스턴스에서 정적 메소드를 호출 할 수 있습니다. 그것은 다른 것입니다. 정적 메소드가 인터페이스를 구현 한 경우 인스턴스없이 인터페이스를 호출 할 수 있습니다. 그것은 말이되지 않는 것입니다. 인터페이스에 구현할 수는 없으며 클래스에 있어야합니다. 따라서 해당 인터페이스를 구현하기 위해 5 개의 서로 다른 클래스가 정의되어 있고 각각이이 정적 메소드의 다른 구현이있는 경우 컴파일러에서 사용할 수있는 것은 무엇입니까?
Charles Bretana

1
제네릭의 경우 @CharlesBretana는 전달 된 유형에 대한 것입니다. 정적 메소드에 대한 인터페이스를 갖는 데 많은 유용성이 있습니다 (또는 원하는 경우 "정적 인터페이스"라고 함) 그리고 인스턴스 인터페이스). 따라서 인스턴스가 없어도 구현 내부를 Whatever<T>() where T:IMyStaticInterface호출 Whatever<MyClass>()하고 가질 수 있습니다. 호출 할 메소드는 런타임에 결정됩니다. 리플렉션을 통해이 작업을 수행 할 수 있지만 강제 할 "계약"은 없습니다. T.MyStaticMethod()Whatever<T>()
Jcl

4

제네릭이 아닌 컨텍스트에서 사용되는 정적 메소드와 관련하여 인터페이스에 대한 참조가 있으면 호출 할 수 없으므로 인터페이스에서 허용하는 것이 의미가 없다는 데 동의합니다. 그러나 다형성 컨텍스트가 아니라 일반적인 인터페이스를 사용하여 작성된 언어 설계에는 근본적인 허점이 있습니다. 이 경우 인터페이스는 인터페이스가 아니라 제약 조건입니다. C #에는 인터페이스 외부의 제약 조건이 없으므로 실질적인 기능이 없습니다. 지목 사항:

T SumElements<T>(T initVal, T[] values)
{
    foreach (var v in values)
    {
        initVal += v;
    }
}

여기에는 다형성이 없으며 제네릭은 실제 유형의 객체를 사용하고 + = 연산자를 호출하지만 해당 연산자가 있는지 확실하게 알 수 없으므로 실패합니다. 간단한 해결책은 제약 조건에서 지정하는 것입니다. 연산자가 정적이고 정적 메서드가 인터페이스에있을 수없고 문제가있는 경우 제약 조건이 인터페이스로 표시되므로 간단한 솔루션은 불가능합니다.

C #에 필요한 것은 실제 제약 조건 유형이며 모든 인터페이스도 제약 조건이지만 모든 제약 조건이 인터페이스는 아니지만 다음과 같이 할 수 있습니다.

constraint CHasPlusEquals
{
    static CHasPlusEquals operator + (CHasPlusEquals a, CHasPlusEquals b);
}

T SumElements<T>(T initVal, T[] values) where T : CHasPlusEquals
{
    foreach (var v in values)
    {
        initVal += v;
    }
}

모든 숫자 유형에 대해 IArithmetic을 구현하는 것에 대해 이미 많은 이야기가 있었지만 제약 조건이 다형성 구조가 아니기 때문에 효율성에 대한 우려가 있습니다 .CArithmetic 제약 조건을 설정하면 문제가 해결됩니다.


C #의 연산자는 정적이므로 여전히 의미가 없습니다.
John Saunders

즉, 제약 조건은 TYPE (인스턴스 아님)에 + 연산자가 있고 (확장자에 의해 + = 연산자가 있음) 템플릿을 생성 할 수 있는지 확인한 다음 실제 템플릿 대체가 발생하면 사용중인 객체가 보장됩니다. 해당 연산자 (또는 다른 정적 메서드)가있는 형식의 값이므로 다음과 같이 말할 수 있습니다. SumElements <int> (0, 5); 이를 인스턴스화합니다 : SumElements (int initVal, int [] values) {foreach (var v in values) {initVal + = v; }} 물론 완벽한 의미
Jeremy Sorensen

1
템플릿이 아님을 기억하십시오. C ++ 템플릿과 동일하지 않은 제네릭입니다.
John Saunders

@ JohnSaunders : 제네릭은 템플릿이 아니며 정적으로 바인딩 된 연산자로 작동하도록 합리적으로 만들 수있는 방법을 모르겠지만 일반적인 컨텍스트에서는 Ta가 정적 이어야 함 을 지정하는 것이 유용 할 수있는 경우가 많이 있습니다 멤버 (예 : T인스턴스 를 생성하는 팩토리 ) 런타임 변경이 없어도 언어가 구문 설탕과 같은 것을 효율적이고 상호 운용 가능한 방식으로 구현할 수 있도록하는 규칙과 도우미 메서드를 정의하는 것이 가능할 것이라고 생각합니다. 가상 정적 메소드가있는 각 인터페이스에 대해 헬퍼 클래스가있는 경우 ...
supercat

1
@ JohnSaunders : 문제는 메소드가 인터페이스를 구현하는 것이 아니라 컴파일러가 선택을 기반으로하는 객체 인스턴스가 없어도 호출 할 가상 메소드를 선택할 수 없다는 것입니다. 해결책은 가상 디스패치 (작동하지 않음)를 사용하지 않고 일반 정적 클래스에 대한 호출을 사용하여 "정적 인터페이스"호출을 전달하는 것입니다. 유형 기반 일반 디스패치에는 인스턴스가 필요하지 않고 유형 만 있습니다.
supercat


3

당신이 원하는 것은 정적 메소드가 Type 또는 해당 유형의 인스턴스를 통해 호출되도록 허용합니다. 이것은 적어도 애매 모호함을 초래할 것이며, 이는 바람직한 특성이 아니다.

중요한지 여부, 최상의 방법 및 성능 문제가 어떤 식 으로든 다른지에 대한 끝없는 논쟁이있을 것입니다. C #을 지원하지 않으면 걱정할 필요가 없습니다.

또한 이러한 요구에 부합하는 컴파일러는 인스턴스와 정적 메소드를보다 엄격하게 분리 할 수있는 일부 최적화를 잃을 수 있습니다.


흥미롭게도 VB.Net의 Type ad Instance (IDE가 후자의 경우 경고를 표시하더라도)에 의해 정적 메소드를 호출하는 것이 가능합니다. 문제가되지 않는 것 같습니다. 최적화에 대해 옳을 수도 있습니다.
Kramii

3

클래스의 정적 메소드와 비 정적 메소드를 서로 다른 인터페이스로 생각할 수 있습니다. 호출되면 정적 메소드는 싱글 톤 정적 클래스 오브젝트로 해석되고 비 정적 메소드는 처리하는 클래스의 인스턴스로 해석됩니다. 따라서 인터페이스에서 정적 및 비 정적 메소드를 사용하는 경우 실제로 하나의 응집성에 액세스하는 데 인터페이스를 사용하려는 경우 두 개의 인터페이스를 효과적으로 선언하게됩니다.


이것은 흥미로운 POV이며 아마도 C # 디자이너가 생각했던 것입니다. 정적 멤버는 지금과 다른 방식으로 생각할 것입니다.
Kramii 2011

3

인터페이스 메소드의 정적 구현이 누락되었거나 Mark Brackett이 "소위 유형 메소드"로 도입 한 예를 제공하려면 다음을 수행하십시오.

데이터베이스 스토리지에서 읽을 때 모든 구조의 테이블에서 읽기를 처리하는 일반 DataTable 클래스가 있습니다. 모든 테이블 특정 정보는 테이블 당 하나의 클래스에 저장되며,이 클래스는 DB의 한 행에 대한 데이터도 보유하며 IDataRow 인터페이스를 구현해야합니다. IDataRow에는 데이터베이스에서 읽을 테이블 구조에 대한 설명이 포함되어 있습니다. DataTable은 DB에서 읽기 전에 IDataRow에서 데이터 구조를 요청해야합니다. 현재 이것은 다음과 같습니다

interface IDataRow {
  string GetDataSTructre();  // How to read data from the DB
  void Read(IDBDataRow);     // How to populate this datarow from DB data
}

public class DataTable<T> : List<T> where T : IDataRow {

  public string GetDataStructure()
    // Desired: Static or Type method:
    // return (T.GetDataStructure());
    // Required: Instantiate a new class:
    return (new T().GetDataStructure());
  }

}

GetDataStructure는 각 테이블을 읽을 때마다 한 번만 필요하며 하나 이상의 인스턴스를 인스턴스화하기위한 오버 헤드는 최소화됩니다. 그러나이 경우에는 좋을 것입니다.


1

참고 : 인터페이스에 대한 확장 메소드를 작성하여 원하는 것과 유사한 동작을 얻을 수 있습니다. 확장 방법은 재정의 할 수없는 공유 정적 동작입니다. 그러나 불행하게도이 정적 방법은 계약의 일부가 아닙니다.


1

인터페이스는 정의 된 사용 가능한 기능의 추상 집합입니다.

해당 인터페이스의 메소드가 정적으로 작동하는지 여부는 인터페이스 뒤에 숨겨져 야하는 구현 세부 사항입니다. . 불필요하게 메소드를 특정 방식으로 구현해야하므로 인터페이스 메소드를 정적으로 정의하는 것은 잘못된 것입니다.

메소드가 정적으로 정의 된 경우 인터페이스를 구현하는 클래스는 캡슐화되지 않을 것입니다. 캡슐화는 객체 지향 디자인에서 노력하는 것이 좋습니다. 이유는 다루지 않겠습니다. http://en.wikipedia.org/wiki/Object-oriented . 이러한 이유로 인터페이스에서는 정적 메서드를 사용할 수 없습니다.


1
예, 인터페이스 선언의 정적은 어리 석습니다. 클래스가 특정 방식으로 인터페이스를 구현하도록 강요해서는 안됩니다. 그러나 C #은 비 정적 메서드를 사용하여 인터페이스를 구현하도록 클래스를 제한합니다. 이게 말이 되요? 왜?
Kramii

키워드 'static'은 사용할 수 없습니다. 정적으로 동작하는 메소드를 작성하기 위해 static 키워드가 필요하지 않기 때문에 제한은 없습니다. 객체의 멤버에 액세스하지 않는 메소드를 작성하면 정적 메소드처럼 작동합니다. 따라서 제한이 있습니다.
Scott Langham

True Scott이지만 코드의 다른 부분에서 누군가가 정적 방식으로 해당 메소드에 액세스 할 수 없습니다. 내가 원하는 이유를 생각할 수는 없지만 OP가 요구하는 것 같습니다.
Chris Marasti-Georg

글쎄, 당신이 정말로 필요하다고 느낀다면 코드의 다른 부분에서 액세스하기 위해 정적 메소드로 작성하고 정적 메소드를 호출하기 위해 비 정적 메소드를 작성할 수 있습니다. 나는 틀릴 수는 있지만 그것이 질문자의 목표라고 의심합니다.
Scott Langham

1

정적 클래스는 일반적으로 사용할 수 있도록이 작업을 수행 할 수 있어야합니다. 원하는 결과를 얻으려면 싱글 톤을 대신 구현해야했습니다.

"사용자", "팀"등과 같은 각 엔티티 유형에 대해 "작성", "읽기", "업데이트", "삭제"와 같은 CRUD 메소드를 구현 한 많은 정적 비즈니스 계층 클래스가있었습니다. 그런 다음 기본을 작성했습니다. CRUD 메소드를 구현 한 비즈니스 계층 클래스에 대한 추상 특성이있는 제어. 이를 통해 기본 클래스에서 "만들기", "읽기", "업데이트", "삭제"작업을 자동화 할 수있었습니다. 정적 제한으로 인해 싱글 톤을 사용해야했습니다.


1

대부분의 사람들은 OOP에서 클래스도 객체이기 때문에 어떤 이유로 c #이 "정적 메소드"를 호출하는 메시지를 가지고 있다는 것을 잊는 것 같습니다. 인스턴스 객체와 클래스 객체 사이에 차이가 있다는 사실은 언어의 결함이나 단점 만 보여줍니다. 그래도 C #에 대한 낙관론 ...


2
문제는이 질문이 "in OOP"에 관한 것이 아니라는 것입니다. "in C #"에 관한 것입니다. C #에는 "메시지"가 없습니다.
John Saunders

1

여기에 '유형 방법'이 필요한 예가 있습니다. 일부 소스 XML을 기반으로 클래스 집합 중 하나를 만들고 있습니다. 그래서 나는

  static public bool IsHandled(XElement xml)

각 클래스에서 차례로 호출되는 함수입니다.

그렇지 않으면 부적절한 객체를 만드는 데 시간이 낭비되므로 함수는 정적이어야합니다. @Ian Boyde가 지적했듯이 팩토리 클래스에서 수행 할 수 있지만 복잡성을 추가합니다.

클래스 구현자가 강제로 구현하도록 인터페이스에 추가하는 것이 좋습니다. 이는 상당한 오버 헤드를 유발하지는 않습니다. 컴파일 / 링크 시간 검사일 뿐이며 vtable에는 영향을 미치지 않습니다.

그러나 그것은 또한 약간의 개선이 될 것입니다. 메소드가 정적이므로 호출자로서 명시 적으로 호출해야하므로 구현되지 않은 경우 즉시 컴파일 오류가 발생합니다. 인터페이스에서 지정하도록 허용하면 개발주기 초기에이 오류가 발생하지 않았지만 다른 깨진 인터페이스 문제와 비교하면 사소한 것입니다.

따라서 균형을 잡는 것이 가장 좋은 미미한 기능입니다.


1

정적 요소를 사용하여 클래스의 특수 인스턴스를 작성하여 Microsoft가 C #에서 정적 클래스를 구현한다는 사실은 정적 기능이 달성되는 방식의 이상입니다. 이론적 인 포인트는 아닙니다.

인터페이스는 클래스 인터페이스의 설명자이거나 상호 작용하는 방법이어야하며 정적 상호 작용을 포함해야합니다. Meriam-Webster의 인터페이스에 대한 일반적인 정의 : 서로 다른 사물이 서로 만나고 서로 소통하거나 서로 영향을 미치는 장소 또는 영역. 클래스 또는 정적 클래스의 정적 구성 요소를 완전히 생략하면 이러한 나쁜 소년의 상호 작용 방식에 대한 많은 부분을 무시합니다.

정적 클래스가있는 인터페이스를 사용할 수있는 곳이 매우 유용한 예는 다음과 같습니다.

public interface ICrudModel<T, Tk>
{
    Boolean Create(T obj);
    T Retrieve(Tk key);
    Boolean Update(T obj);
    Boolean Delete(T obj);
}

현재, 나는 아무것도 잊지 않고 있는지 확인하기 위해 어떤 종류의 검사없이 이러한 메소드를 포함하는 정적 클래스를 작성합니다. OOP 이전의 나쁜 프로그래밍 시대와 같습니다.


이것은 고대의 질문입니다. 요즘 우리는 정식으로 대답하기가 어렵 기 때문에 의견 기반 질문을 피하기 위해 매우 열심히 노력합니다. 이것에 대답 할 수있는 유일한 좋은 방법은 MS가 그랬던 것처럼 행동 한 이유를 설명하는 것입니다.
Nathan Tuggy

1

C #과 CLR은 Java와 마찬가지로 인터페이스에서 정적 메서드를 지원해야합니다. 정적 수정자는 계약 정의의 일부이며, 특히 거동 및 반환 값이 호출마다 다를 수 있지만 인스턴스에 따라 다르지 않음을 의미합니다.

즉, 인터페이스에서 정적 메서드를 사용하려고 할 때 사용할 수 없으면 주석을 대신 사용하는 것이 좋습니다. 원하는 기능을 얻을 수 있습니다.


0

나는 짧은 대답은 "제로 유용성이 없기 때문에"라고 생각합니다. 인터페이스 메소드를 호출하려면 유형의 인스턴스가 필요합니다. 인스턴스 메소드에서 원하는 정적 메소드를 호출 할 수 있습니다.


0

나는 C #이 이런 종류의 상황을 위해 다른 키워드가 필요하다는 사실에 의문을 제기하고 있다고 생각합니다. 리턴 값이 호출 된 유형에만 의존하는 메소드를 원합니다. 해당 유형을 알 수 없으면 "정적"이라고 부를 수 없습니다. 그러나 유형이 알려지면 정적이됩니다. "정적되지 않은 정적"은 아직 정적 인 것이 아니라 일단 수신 유형을 알면 그대로입니다. 이것은 완벽하게 좋은 개념이므로 프로그래머가 계속 요구합니다. 그러나 디자이너들이 언어에 대해 생각하는 방식에는 맞지 않았습니다.

사용할 수 없으므로 아래 표시된 방식으로 비 정적 방법을 사용했습니다. 정확히 이상적이지는 않지만 적어도 나에게는 적합하지 않은 접근법을 볼 수 없습니다.

public interface IZeroWrapper<TNumber> {
  TNumber Zero {get;}
}

public class DoubleWrapper: IZeroWrapper<double> {
  public double Zero { get { return 0; } }
}

0

객체 지향 개념에 따라 클래스에 의해 구현되고 객체를 사용하여 이러한 구현 된 함수에 액세스하는 계약이 있습니다.

따라서 인터페이스 계약 메소드에 액세스하려면 오브젝트를 작성해야합니다. 정적 메소드의 경우 항상 허용되지 않아야합니다. 정적 클래스, 메소드 및 변수는 해당 영역 (또는 클래스)의 객체를 만들지 않고 객체를 요구하지 않고 메모리에로드하거나 객체 생성이 필요하지 않다고 말할 수 있습니다.


0

개념적 으로 인터페이스가 정적 메소드를 포함하는 계약을 정의 할 수없는 이유는 없습니다.

현재 C # 언어 구현의 경우 제한은 기본 클래스 및 인터페이스의 상속이 허용되기 때문입니다. "클래스 SomeBaseClass"가 "인터페이스 ISomeInterface"를 구현하고 "클래스 SomeDerivedClass : SomeBaseClass, ISomeInterface"도 인터페이스를 구현하는 경우, 정적 메소드는 인스턴스 메소드와 동일한 서명을 가질 수 없으므로 인터페이스 메소드를 구현하는 정적 메소드 컴파일에 실패합니다. 인터페이스를 구현하려면 기본 클래스에 있어야합니다.

정적 클래스는 기능적으로 싱글 톤과 동일하며 구문이 깔끔한 싱글 톤과 동일한 목적으로 사용됩니다. 싱글 톤이 인터페이스를 구현할 수 있으므로 정적에 의한 인터페이스 구현은 개념적으로 유효합니다.

따라서 단순히 상속에서 동일한 이름의 정적 메소드와 정적 메소드에 대한 C # 이름 충돌의 한계로 간단히 요약됩니다. 정적 메소드 계약 (인터페이스)을 지원하기 위해 C #을 "업그레이드"할 수없는 이유는 없습니다.


-1

클래스가 인터페이스를 구현하면 인터페이스 멤버를위한 인스턴스가 생성됩니다. 정적 유형에는 인스턴스가 없지만 인터페이스에 정적 서명이있을 필요는 없습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.