c# 사용이유 - 일반적인 정적 클래스에서 확장 메서드를 선언 할 수없는 이유는 무엇입니까?





최상위 정의 (6)


매우 흥미로운 질문인데 정적 제네릭 클래스를 사용하려고 시도한 적은 없지만 최소한 가능할 것으로 보인다.

확장 메서드 선언의 컨텍스트에서 특정 범용 형식 (예 : IEnumerable<T> )에 대한 확장 메서드를 선언 할 수있을뿐만 아니라 형식 매개 변수 T 를 식에 가져올 수 있습니다. IEnumerable<int>IEnumerable<string> 을 다른 유형으로 처리하는 데 동의하는 경우 이는 개념적 차원에서도 의미가 있습니다.

정적 제네릭 클래스에서 확장 메서드를 선언 할 수있게되면 IEnumerable<T> where T : IComparable 대한 모든 확장 메서드를 효과적으로 그룹화 IEnumerable<T> where T : IComparable 함께 그룹화하여 반복해서 형식 매개 변수 제약 조건을 반복하지 IEnumerable<T> where T : IComparable 됩니다.

명세 (인용문)에 따르면, 확장 메소드는 정적이 아닌 중첩 클래스와 일반이 아닌 클래스에서만 선언 될 수 있습니다. 처음 두 제약 조건의 이유는 매우 분명합니다.

  1. 믹스 인이 아니라 구문 론적 인 설탕이므로 어떤 상태도 지니고 있지 않을 수 있습니다.
  2. 확장 기능을 제공하는 유형과 동일한 어휘 범위를 가져야합니다.

비 제네릭 정적 클래스에 대한 제한은 다소 자의적으로 보입니다. 여기서 기술적 인 이유를 생각해 낼 수는 없습니다. 그러나 언어 디자이너가 확장 메서드를 제공하려는 제네릭 클래스의 형식 매개 변수에 의존하는 확장 메서드를 작성하는 것을 방해하기로 결정했을 수 있습니다. 대신 확장 메소드의 진정한 일반적인 구현을 제공하기를 원하지만 일반적인 구현 이외에 확장 메소드의 최적화 된 / 특수화 된 (컴파일 타임 바운드) 버전을 제공 할 수 있기를 바랍니다.

C ++에서 템플릿 전문화를 생각 나게합니다. 편집 : 불행히도 이것은 잘못되었습니다, 내 추가 사항을 아래에 참조하십시오.

좋아, 이것이 정말로 흥미로운 주제이기 때문에 좀 더 연구를했다. 실제로 여기에서 놓친 기술적 제한이 있습니다. 몇 가지 코드를 살펴 보겠습니다.

public static class Test
{
    public static void DoSomething<T>(this IEnumerable<T> source)
    {
        Console.WriteLine("general");
    }
    public static void DoSomething<T>(this IEnumerable<T> source) where T :IMyInterface
    {
        Console.WriteLine("specific");
    }
}

이것은 실제로이 컴파일러 오류로 실패합니다.

'ConsoleApplication1.Test'유형은 이미 'DoSomething'이라는 멤버를 동일한 매개 변수 유형으로 정의합니다.

다음으로 두 개의 다른 확장 클래스로 분할 해 봅니다.

public interface IMyInterface { }
public class SomeType : IMyInterface {}

public static class TestSpecific
{
    public static void DoSomething<T>(this IEnumerable<T> source) where T : IMyInterface 
    {
        Console.WriteLine("specific");
    }
}
public static class TestGeneral
{
    public static void DoSomething<T>(this IEnumerable<T> source)
    {
        Console.WriteLine("general");
    }
}

class Program
{
    static void Main(string[] args)
    {
        var general = new List<int>();
        var specific = new List<SomeType>();

        general.DoSomething();
        specific.DoSomething();

        Console.ReadLine();
    }
}

나의 처음 인상 (그것은 어제 밤 늦었다)에 반하여, 이것은 콜 사이트에서 모호함을 가져올 것이다. 이러한 모호성을 해결하기 위해 전통적인 방법으로 확장 메서드를 호출해야하지만 이는 우리의 의도에 위배됩니다.

따라서 확장 메소드에 대한 컴파일 타임 바인딩 제네릭 특수화를 선언 할 수없는 상황이 발생합니다. 반면에 특별한 특별한 제네릭 타입 매개 변수에 대해서만 확장 메서드를 선언 할 수있는 이유는 여전히 존재하지 않습니다. 따라서 정적 제네릭 클래스에서 선언하는 것이 좋을 것입니다.

다른 한편으로는 다음과 같은 확장 메소드를 작성합니다.

    public static void DoSomething<T>(this IEnumerable<T> source) where T : IMyInterface {}

또는

    public static void DoSomething(this IEnumerable<IMyInterface> source) {}

모두 너무 다르지 않고 호출 방식에 비해 약간의 캐스팅 만 필요합니다 (특정 유형에 따라 최적화를 구현할 것이므로 T를 IMyInterface 또는 기타 형식으로 변환해야합니다) . 그래서 제가 생각해 낼 수있는 유일한 이유는 다시입니다. 언어 디자이너는 일반적인 확장 기능을 진정으로 일반적인 방식으로 작성하도록 권장하려고합니다.

C # 4.0에서 소개 될 방정식으로 공 / 반항을 취하면 흥미로운 점이 발생할 수 있습니다.

몇 가지 일반적인 클래스에 대한 확장 메서드를 많이 만들고 싶습니다.

public class SimpleLinkedList<T> where T:IComparable

그리고 저는 다음과 같은 메소드를 만들기 시작했습니다 :

public static class LinkedListExtensions
{
    public static T[] ToArray<T>(this SimpleLinkedList<T> simpleLinkedList) where T:IComparable
    {
       //// code
    }
}

하지만 LinkedListExtensions 클래스를 다음과 같이 일반화하려고 시도했을 때 :

public static class LinkedListExtensions<T> where T:IComparable
{
    public static T[] ToArray(this SimpleLinkedList<T> simpleLinkedList)
    {
         ////code
    }
}

"확장 메서드는 비 제네릭, 중첩되지 않은 정적 클래스에서만 선언 할 수 있습니다."

그리고 나는이 제한이 어디에서 왔는지를 추측하려고 노력하고 있으며 아이디어가 없습니다.

편집 : 아직도 문제의 명확한 비전을 가지고 있지 않습니다. 어떤 이유로 든 구현되지 않은 것 같습니다.




일반적으로 확장 메서드를 사용할 때 클래스를 지정하지 않으므로 컴파일러는 확장 메서드가 정의 된 클래스를 컴파일러에서 알 수 없습니다.

static class GenStatic<T>
{
  static void ExtMeth(this Class c) {/*...*/}
}

Class c = new Class();
c.ExtMeth(); // Equivalent to GenStatic<T>.ExtMeth(c); what is T?

확장 메서드 자체는 일반적인 것일 수 있으므로 전혀 문제가되지 않습니다.

static class NonGenStatic
{
  static void GenExtMeth<T>(this Class c) {/*...*/}
}

Class c = newClass();
c.ExtMeth<Class2>(); // Equivalent to NonGenStatic.ExtMeth<Class2>(c); OK

정적 클래스가 generic이 아니지만 generic 메서드가되도록 예제를 쉽게 다시 작성할 수 있습니다. 실제로 이것은 Enumerable 과 같은 .NET 클래스가 작성되는 방법입니다.

  public static class LinkedListExtensions
  {
    public static T[] ToArray<T>(this SimpleLinkedList<T> where T:IComparable simpleLinkedList)
    {
      // code
    }
  }



그냥 생각했지만이 클래스에서 파생되지 않고 확장 메서드 모음을 작성하는 것보다 특수화 된 메서드를 추가 할 수있는 이유가 있습니까? 나는 당신이 당신의 이유를 가지고 있지만 거기에 그것을 던지고 있다고 확신합니다.




확장 메서드가 포함 된 정적 클래스에 연결되는 것으로 확장 메서드를 생각하지 마십시오. 대신 특정 네임 스페이스에 묶여 있다고 생각하십시오. 따라서 정의 된 정적 클래스는 단순히 네임 스페이스 내에서 이러한 메서드를 선언하는 데 사용되는 셸입니다. 또한 여러 유형의 확장 메소드에 대해 여러 클래스를 작성할 수는 있지만 클래스 자체를 확장 메소드를 명확하게 그룹화하는 방법 이상으로 생각해서는 안됩니다.

확장 메서드는 포함 된 클래스의 특성을 확장하지 않습니다. 메소드의 서명은 확장 메소드에 대한 모든 것을 정의합니다. 그리고 당신이 LinkedListExtensions.ToArray(...) 같은 메서드를 호출 할 수있는 동안, 나는 그것이 확장 메서드에 대한 의도라고 생각하지 않습니다. 따라서 프레임 워크 작성자는 확장 메소드가 자체 포함되어 있고 클래스가 상주하는 클래스에 직접 연결되어 있지 않다는 것을 개발자에게 알리는 방법으로 만난 제한을 작성했다고 생각합니다.




문제는 컴파일러가 확장명을 어떻게 결정합니까?

설명하는 두 가지 방법을 모두 정의한다고 가정 해보십시오.

public static class LinkedListExtensions {
    public static T[] ToArray<T>(this SimpleLinkedList<T> simpleLinkedList) where T:IComparable {
        //// code
    }
}
public static class LinkedListExtensions<T> where T:IComparable {
    public static T[] ToArray(this SimpleLinkedList<T> simpleLinkedList) {
        ////code
    }
}

다음의 경우에 어떤 방법이 사용됩니까?

SimpleLinkedList<int> data = new SimpleLinkedList<int>();
int[] dataArray = data.ToArray();

내 생각 엔이 시나리오를 피하기 위해 언어 디자이너가 확장 메서드를 비 제네릭 형식으로 제한하기로 결정한 것 같습니다.




다음과 같이 구현할 수 있습니다.

@SuppressWarnings("unchecked")
public <T extends Animal> T callFriend(String name) {
    return (T)friends.get(name);
}

(네, 이것은 합법적 인 코드입니다 ( Java Generics : 반환 유형으로 정의 된 일반 유형 참조).

반환 유형은 호출자에서 유추됩니다. 그러나 @SuppressWarnings 주석을 참고하십시오 : 이 코드는 typesafe가 아니라는 것을 알려 줍니다 . 직접 확인하거나 런타임에 ClassCastExceptions 을 얻을 수 있습니다.

불행히도, (임시 변수에 반환 값을 할당하지 않고) 그것을 사용하는 방법은 컴파일러를 행복하게 만드는 유일한 방법은 다음과 같이 호출하는 것입니다.

jerry.<Dog>callFriend("spike").bark();

이것은 캐스팅보다 조금 좋을 수도 있지만, David Schmitt가 말했듯이 Animal 클래스에 abstract talk() 메서드를 사용하는 것이 좋습니다.





c# generics static extension-methods