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;
}
....
}
답변
왜이 작업을 수행 할 수 없는지 묻는다고 가정합니다.
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을 절약하고 표시 할 수 있습니다. 좋은 생각이 아닙니다.
답변
필자의 기술적 인 이유는 정적 메소드가 vtable에 없으며 호출 사이트가 컴파일 타임에 선택되기 때문입니다. 재정의 또는 가상 정적 멤버를 가질 수없는 것과 같은 이유입니다. 자세한 내용을 보려면 CS 등급 또는 컴파일러 원이 필요합니다.
정치적 이유로 나는 워커 루 대학에서 수학, 컴퓨터 과학 및 응용 수학 학사 학위를 소지 한 에릭 리퍼 ( Eric Lippert)를 인용 할 것이다 (출처 : LinkedIn ) :
정적 메소드의 핵심 디자인 원칙, 그 이름을 부여하는 원칙 … 컴파일 타임에 어떤 메소드가 호출 될지 항상 정확하게 결정할 수 있습니다. 즉,이 방법은 코드의 정적 분석에 의해서만 해결 될 수 있습니다.
Lippert는 소위 유형 방법을위한 공간을 남겨 둡니다.
즉, (정적과 같은) 유형과 관련된 메서드는 null이 불가능한 “this”인수 (인스턴스 또는 가상과는 달리)를 사용하지 않지만 호출 된 메서드는 T의 생성 된 유형 ( 컴파일 타임에 결정 해야하는 정적과는 달리).
그러나 그 유용성에 대해서는 아직 확신이 없습니다.
답변
여기서 대부분의 대답은 요점을 놓친 것 같습니다. 다형성은 인스턴스 간뿐만 아니라 유형 간에도 사용할 수 있습니다. 이것은 제네릭을 사용할 때 종종 필요합니다.
제네릭 메서드에 형식 매개 변수가 있고 해당 작업을 수행해야한다고 가정합니다. 우리는 생성자를 알지 못하기 때문에 인스턴스화하고 싶지 않습니다.
예를 들면 다음과 같습니다.
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...
}
}
}
이것은 분명히 추악하고 불필요한 것은 다른 모든 방법의 코드를 복잡하게 만듭니다. 분명히, 우아한 해결책도 아닙니다!
답변
나는 그것이 오래된 질문이라는 것을 알고 있지만 흥미 롭습니다. 예가 최고가 아닙니다. 사용 사례를 보여 주면 훨씬 더 명확 할 것이라고 생각합니다.
문자열 DoSomething <T> () 여기서 T : ISomeFunction { if (T.someFunction ()) ... }
정적 메소드 가 인터페이스를 구현 하도록하는 것만으로는 원하는 것을 달성 할 수 없습니다. 필요한 것은 인터페이스의 일부로 정적 멤버를 갖는 것 입니다. 특히 물건을 만들 수있을 때 많은 유스 케이스를 상상할 수 있습니다. 내가 제공 할 수있는 두 가지 접근법은 도움이 될 수 있습니다.
- 위의 DoSomething에 전달할 형식이 type 매개 변수 인 정적 제네릭 클래스를 만듭니다. 이 클래스의 각 변형에는 해당 유형과 관련된 항목을 보유하는 하나 이상의 정적 멤버가 있습니다. 이 정보는 각 관심 클래스에 “정보 등록”루틴을 호출하거나 클래스 변형의 정적 생성자가 실행될 때 정보를 얻기 위해 Reflection을 사용하여 제공 할 수 있습니다. 후자의 접근법은 Comparer <T> .Default ()와 같은 것들에 의해 사용된다고 생각합니다.
- 관심있는 각 클래스 T에 대해 IGetWhateverClassInfo <T>를 구현하고 “새”제한 조건을 충족시키는 클래스 또는 구조체를 정의하십시오. 클래스에는 실제로 필드가 포함되지 않지만 유형 정보가 포함 된 정적 필드를 반환하는 정적 속성이 있습니다. 해당 클래스 또는 구조체의 유형을 해당 일반 루틴으로 전달하면 인스턴스를 작성하고이를 사용하여 다른 클래스에 대한 정보를 얻을 수 있습니다. 이 목적으로 클래스를 사용하는 경우 매번 새 디스크립터 오브젝트 인스턴스를 구성하지 않아도되도록 위에 표시된대로 정적 제네릭 클래스를 정의해야합니다. 구조체를 사용하는 경우 인스턴스화 비용은 없어야하지만 모든 구조체 유형마다 DoSomething 루틴의 다른 확장이 필요합니다.
이러한 접근법 중 어느 것도 실제로 매력적이지 않습니다. 반면에 CLR에 이러한 종류의 기능을 제공하기 위해 메커니즘이 존재한다면 .net은 매개 변수화 된 “새로운”제약 조건을 지정할 수있게 될 것입니다. 특정 서명이있는 정적 메소드가 있는지 알기가 어렵습니다.)
답변
근시안적인 것 같아요.
원래 설계되었을 때 인터페이스는 클래스 인스턴스에서만 사용되도록 설계되었습니다
IMyInterface val = GetObjectImplementingIMyInterface();
val.SomeThingDefinedinInterface();
제네릭에 대한 제약 조건이 인터페이스에 정적 메서드를 추가했을 때 인터페이스가 실제로 사용 되었기 때문에 인터페이스가 도입 된 것입니다.
(댓글에 응답 🙂 이제 변경하려면 CLR을 변경해야하므로 기존 어셈블리와 호환되지 않을 수 있습니다.
답변
인터페이스는 객체의 동작을 지정합니다.
정적 메서드는 객체의 동작을 지정하지 않지만 어떤 방식으로 객체에 영향을주는 동작입니다.
답변
인터페이스가 “계약”을 나타내는 한 정적 클래스가 인터페이스를 구현하는 것이 조용해 보입니다.
위의 주장은 모두 계약에 관한 요점을 놓친 것 같습니다.