[C#] 기존 정적 클래스에 확장 메소드를 추가 할 수 있습니까?

C #에서 확장 메서드를 좋아하지만 콘솔과 같은 정적 클래스에 확장 메서드를 추가하는 데 성공하지 못했습니다.

예를 들어 콘솔에 ‘WriteBlueLine’이라는 확장을 추가하고 싶다면 다음을 수행하십시오.

Console.WriteBlueLine("This text is blue");

콘솔을 ‘this’매개 변수로 사용하여 로컬 공개 정적 메소드를 추가하여 시도했지만 주사위는 없습니다!

public static class Helpers {
    public static void WriteBlueLine(this Console c, string text)
    {
        Console.ForegroundColor = ConsoleColor.Blue;
        Console.WriteLine(text);
        Console.ResetColor();
    }
}

이것은 콘솔에 ‘WriteBlueLine’메서드를 추가하지 않았습니다 … 잘못하고 있습니까? 아니면 불가능을 요구합니까?



답변

아니요. 확장 메서드에는 개체의 인스턴스 변수 (값)가 필요합니다. 그러나 ConfigurationManager인터페이스 주위에 정적 랩퍼를 작성할 수 있습니다 . 랩퍼를 구현하는 경우 메소드를 직접 추가 할 수 있으므로 확장 메소드가 필요하지 않습니다.

 public static class ConfigurationManagerWrapper
 {
      public static ConfigurationSection GetSection( string name )
      {
         return ConfigurationManager.GetSection( name );
      }

      .....

      public static ConfigurationSection GetWidgetSection()
      {
          return GetSection( "widgets" );
      }
 }


답변

C #에서 클래스에 정적 확장을 추가 할 수 있습니까? 아니요, 그러나 당신은 이것을 할 수 있습니다 :

public static class Extensions
{
    public static T Create<T>(this T @this)
        where T : class, new()
    {
        return Utility<T>.Create();
    }
}

public static class Utility<T>
    where T : class, new()
{
    static Utility()
    {
        Create = Expression.Lambda<Func<T>>(Expression.New(typeof(T).GetConstructor(Type.EmptyTypes))).Compile();
    }
    public static Func<T> Create { get; private set; }
}

작동 방식은 다음과 같습니다. 정적 확장 메서드를 기술적으로 작성할 수는 없지만이 코드는 확장 메서드의 허점을 악용합니다. 허점은 null 예외를 얻지 않고 null 객체에서 확장 메서드를 호출 할 수 있다는 것입니다 (@this를 통해 아무것도 액세스하지 않는 한).

이것을 사용하는 방법은 다음과 같습니다.

    var ds1 = (null as DataSet).Create(); // as oppose to DataSet.Create()
    // or
    DataSet ds2 = null;
    ds2 = ds2.Create();

    // using some of the techniques above you could have this:
    (null as Console).WriteBlueLine(...); // as oppose to Console.WriteBlueLine(...)

이제 기본 생성자를 예제로 호출하는 이유는 무엇이며 왜 Expression 가비지를 모두 수행하지 않고 첫 번째 코드 스 니펫에서 새 T ()를 반환하지 않습니까? 오늘은 운이 좋은 날인데 2fer를 얻었 기 때문입니다. 고급 .NET 개발자가 알고 있듯이 새 T ()는 리플렉션을 사용하여 기본 생성자를 가져와 호출하기 전에 System.Activator에 대한 호출을 생성하므로 속도가 느립니다. 젠장, 마이크로 소프트! 그러나 내 코드는 객체의 기본 생성자를 직접 호출합니다.

정적 확장은 이것보다 낫지 만 절박한 시간에는 필사적 인 조치가 필요합니다.


답변

불가능합니다.

그리고 네, MS는 여기서 실수를 한 것 같습니다.

그들의 결정은 의미가 없으며 프로그래머가 (위에서 설명한 것처럼) 무의미한 래퍼 클래스를 작성하도록 강요합니다.

다음은 좋은 예입니다. 정적 MS 단위 테스트 클래스를 확장하려고 시도합니다. Assert : 1 개 이상의 Assert 메소드가 필요 AreEqual(x1,x2)합니다.

이를 수행 할 수있는 유일한 방법은 다른 클래스를 가리 키거나 약 100 개의 다른 Assert 메소드를 래퍼로 작성하는 것입니다. 왜!?

인스턴스 확장을 허용하기로 결정한 경우 정적 확장을 허용하지 않는 논리적 인 이유가 없습니다. 인스턴스를 확장 할 수 있으면 라이브러리 섹션화에 대한 논쟁은 일어나지 않습니다.


답변

OP와 같은 질문에 대한 답을 찾으려고 노력 하면서이 스레드를 우연히 발견했습니다. 원하는 답변을 찾지 못했지만 결국이 작업을 수행했습니다.

public static class MyConsole
{
    public static void WriteLine(this ConsoleColor Color, string Text)
    {
        Console.ForegroundColor = Color;
        Console.WriteLine(Text);
    }
}

그리고 나는 이것을 다음과 같이 사용합니다 :

ConsoleColor.Cyan.WriteLine("voilà");


답변

어쩌면 사용자 정의 네임 스페이스와 동일한 클래스 이름으로 정적 클래스를 추가 할 수 있습니다.

using CLRConsole = System.Console;

namespace ExtensionMethodsDemo
{
    public static class Console
    {
        public static void WriteLine(string value)
        {
            CLRConsole.WriteLine(value);
        }

        public static void WriteBlueLine(string value)
        {
            System.ConsoleColor currentColor = CLRConsole.ForegroundColor;

            CLRConsole.ForegroundColor = System.ConsoleColor.Blue;
            CLRConsole.WriteLine(value);

            CLRConsole.ForegroundColor = currentColor;
        }

        public static System.ConsoleKeyInfo ReadKey(bool intercept)
        {
            return CLRConsole.ReadKey(intercept);
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                Console.WriteBlueLine("This text is blue");
            }
            catch (System.Exception ex)
            {
                Console.WriteLine(ex.Message);
                Console.WriteLine(ex.StackTrace);
            }

            Console.WriteLine("Press any key to continue...");
            Console.ReadKey(true);
        }
    }
}


답변

C # 7부터는 지원되지 않습니다. 그러나 C # 8같은 것을 통합하고 지원할 가치가있는 제안 에 대한 논의 가 있습니다 .


답변

아니. 확장 메소드 정의에는 확장하려는 유형의 인스턴스가 필요합니다. 불행한; 왜 필요한지 모르겠습니다 …