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





3 Answers

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

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

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();

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

최상위 정의 해야

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

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
    }
}

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

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

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




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

확장 메서드 선언의 컨텍스트에서 특정 범용 형식 (예 : 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에서 소개 될 방정식으로 공 / 반항을 취하면 흥미로운 점이 발생할 수 있습니다.




정적 클래스는 추상으로 정의 할 수 있어야합니다. 그들이 이것을 할 수 있다면 직접 사용할 수는 없지만 일반 클래스처럼 상속 받아야하고 일반 정적 클래스를 확장 클래스에 적용 할 수 있으므로 일반 정적 클래스를 상속 받아야한다고 지정할 수 있습니다. 그 계급을 상속하고, 모든 것이 제대로 작동 할 것입니다.

지금 당장은 정적 클래스에는 상당한 제한이 있습니다. 이것은 많은 경우에 모조를 실제로 망쳐 놓은 것입니다. 컴파일러가 반환 형식을 유추 할 수 없기 때문에 메소드 서명에 제네릭을 모두 사용하지 않는 제네릭 함수를 호출하기 위해 전체 generic 구현을 입력해야하므로이 항목이 약화되고 많은 부분을 차지합니다. 거기에있을 필요가없는 타이핑.

(두 번째 generic이 첫 번째 정의에 정의되어 있고 컴파일 타임에 추측하는 것이 분명하지만 사용하지 않는 경우도 있습니다.이 것은 매우 명확하기 때문에 매우 짜증납니다.) MS는 제네릭에 대해 많은 노력을 기울여 이러한 기능을 향상시킬 수 있습니다.






Related