문제

나는 C#의 확장 메소드의 팬이지만 콘솔과 같은 정적 클래스에 확장 방법을 추가하는 데 성공하지 못했습니다.

예를 들어, 'WriteBlueline'이라는 콘솔에 확장을 추가하려면 다음을 수행 할 수 있습니다.

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

나는 콘솔을 '이'매개 변수로 사용하는 로컬, 공개 정적 방법을 추가하여 이것을 시도했지만 주사위는 없습니다!

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 예외를 얻지 않고 널 객체에서 확장 메소드를 호출 할 수 있다는 것입니다 (@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(...)

이제 왜 기본 생성자를 예제로 호출 한 이유를 뽑았는데, 그 표현식 쓰레기를 모두하지 않고 첫 번째 코드 스 니펫에서 새로운 t ()를 반환하지 않는 이유는 무엇입니까? 당신이 2fer를 얻었 기 때문에 당신의 운이 좋은 날. Advanced .NET 개발자가 알고 있듯이 New T ()는 반사를 사용하여 호출하기 전에 기본 생성자를 가져 오기 위해 System.Activator에 대한 호출을 생성하기 때문에 느립니다. 젠장, 마이크로 소프트! 그러나 내 코드는 객체의 기본 생성자를 직접 호출합니다.

정적 확장은 이것보다 낫지 만 절망적 인 시간은 절망적 인 조치를 요구합니다.

불가능합니다.

그리고 네 MS가 여기서 실수를 한 것 같아요.

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

좋은 예는 다음과 같습니다. 정적 MS 장치 테스트 클래스를 확장하려는 시도 : 1 더 ASSERT 방법을 원합니다. AreEqual(x1,x2).

이를 수행하는 유일한 방법은 다른 클래스를 가리키거나 100 대의 다른 어제 방법에 대한 래퍼를 작성하는 것입니다. 왜!?

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

나는 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에서 이와 같은 통합에 대한 토론 그리고 지원할 가치가있는 제안.

확장 방법의 경우, 확장 방법 자체는 정적입니다. 그러나 인스턴스 방법 인 것처럼 호출됩니다. 정적 클래스는 인스턴스화 할 수 없으므로 연장 메소드를 호출 할 클래스의 인스턴스가 없을 것입니다. 이러한 이유로 컴파일러는 정적 클래스에 대해 확장 방법을 정의 할 수 없습니다.

Obnoxious는 다음과 같이 썼다. "고급 .NET 개발자가 알고 있듯이 새로운 t ()는 반사를 사용하여 호출하기 전에 기본 생성자를 가져 오기 위해 반사를 사용하는 호출을 생성하기 때문에 느린다"고 썼다.

New ()는 유형이 컴파일 시간에 알려진 경우 IL "NewOBJ"명령어로 컴파일됩니다. NewOBJ는 직접 호출을위한 생성자를 가져옵니다. System.Activator.CreateInstance ()로 Call에 Calling System.Activator.createInstance ()를 호출하려면 IL "Call"명령으로 컴파일합니다. 새로운 () 일반 유형에 사용하면 System.Activator.createInstance ()를 호출하게됩니다. 이 시점에서 Obnooxious 씨의 글은 불분명했다.

이 코드 :

System.Collections.ArrayList _al = new System.Collections.ArrayList();
System.Collections.ArrayList _al2 = (System.Collections.ArrayList)System.Activator.CreateInstance(typeof(System.Collections.ArrayList));

이 IL을 생성합니다.

  .locals init ([0] class [mscorlib]System.Collections.ArrayList _al,
           [1] class [mscorlib]System.Collections.ArrayList _al2)
  IL_0001:  newobj     instance void [mscorlib]System.Collections.ArrayList::.ctor()
  IL_0006:  stloc.0
  IL_0007:  ldtoken    [mscorlib]System.Collections.ArrayList
  IL_000c:  call       class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
  IL_0011:  call       object [mscorlib]System.Activator::CreateInstance(class [mscorlib]System.Type)
  IL_0016:  castclass  [mscorlib]System.Collections.ArrayList
  IL_001b:  stloc.1

당신은 추가 할 수 없습니다 공전 유형에 대한 방법. 유형의 인스턴스에 만 인스턴스 메소드 만 추가 할 수 있습니다.

의 요점 this 수정자는 C# 컴파일러에게 인스턴스의 왼쪽에 인스턴스를 전달하도록 지시하는 것입니다. . 정적/확장 방법의 첫 번째 매개 변수로서

유형에 정적 메소드를 추가하는 경우 첫 번째 매개 변수에 대한 인스턴스가 없습니다.

확장 방법을 배우고 성공하지 못했을 때 System.environment Back 으로이 작업을 수행하려고했습니다. 다른 사람들이 언급 한 바와 같이, 확장 방법에는 클래스의 인스턴스가 필요하기 때문입니다.

예, 제한된 의미로.

public class DataSet : System.Data.DataSet
{
    public static void SpecialMethod() { }
}

이것은 작동하지만 콘솔은 정적이기 때문에 그렇지 않습니다.

public static class Console
{       
    public static void WriteLine(String x)
    { System.Console.WriteLine(x); }

    public static void WriteBlueLine(String x)
    {
        System.Console.ForegroundColor = ConsoleColor.Blue;
        System.Console.Write(.x);           
    }
}

동일한 네임 스페이스에 있지 않기 때문에 작동합니다. 문제는 System.Console이 가진 모든 방법에 대한 프록시 정적 메소드를 작성해야한다는 것입니다. 다음과 같은 것을 추가 할 수 있기 때문에 반드시 나쁜 것은 아닙니다.

    public static void WriteLine(String x)
    { System.Console.WriteLine(x.Replace("Fck","****")); }

또는

 public static void WriteLine(String x)
    {
        System.Console.ForegroundColor = ConsoleColor.Blue;
        System.Console.WriteLine(x); 
    }

그것이 작동하는 방식은 당신이 표준 쓰기 라인에 무언가를 연결한다는 것입니다. 라인 카운트 또는 잘못된 단어 필터 또는 그 밖의 일 수 있습니다. 네임 스페이스에 콘솔을 지정할 때마다 WebProject1과 네임 스페이스 시스템을 가져 오면 WebProject1.Console은 Namespace WebProject1의 해당 클래스의 기본값으로 System.Console을 선택합니다. 따라서이 코드는 모든 console.writeline 호출을 지정하지 않은 한 System.console.writeline을 지정하지 않는 한 파란색으로 전환합니다.

다음은 An으로 거부되었습니다 편집하다 Tvanfosson의 답변에. 나는 그것을 내 자신의 대답으로 기여하도록 요청 받았다. 나는 그의 제안을 사용하고 ConfigurationManager 싸개. 원칙적으로 나는 단순히 ... TVANFOSSON의 답변에서.

아니요. 확장 방법에는 객체의 인스턴스가 필요합니다. 그러나 configurationManager 인터페이스 주위에 정적 래퍼를 작성할 수 있습니다. 래퍼를 구현하는 경우 메소드를 직접 추가 할 수 있으므로 확장 방법이 필요하지 않습니다.

public static class ConfigurationManagerWrapper
{
    public static NameValueCollection AppSettings
    {
        get { return ConfigurationManager.AppSettings; }
    }

    public static ConnectionStringSettingsCollection ConnectionStrings
    {
        get { return ConfigurationManager.ConnectionStrings; }
    }

    public static object GetSection(string sectionName)
    {
        return ConfigurationManager.GetSection(sectionName);
    }

    public static Configuration OpenExeConfiguration(string exePath)
    {
        return ConfigurationManager.OpenExeConfiguration(exePath);
    }

    public static Configuration OpenMachineConfiguration()
    {
        return ConfigurationManager.OpenMachineConfiguration();
    }

    public static Configuration OpenMappedExeConfiguration(ExeConfigurationFileMap fileMap, ConfigurationUserLevel userLevel)
    {
        return ConfigurationManager.OpenMappedExeConfiguration(fileMap, userLevel);
    }

    public static Configuration OpenMappedMachineConfiguration(ConfigurationFileMap fileMap)
    {
        return ConfigurationManager.OpenMappedMachineConfiguration(fileMap);
    }

    public static void RefreshSection(string sectionName)
    {
        ConfigurationManager.RefreshSection(sectionName);
    }
}

확장 방법을 작성할 수는 없지만 요청하는 동작을 모방 할 수 있습니다.

using FooConsole = System.Console;

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

이렇게하면 다른 클래스에서 Console.writeBlueline (Footext)을 호출 할 수 있습니다. 다른 클래스가 콘솔의 다른 정적 기능에 대한 액세스를 원한다면 네임 스페이스를 통해 명시 적으로 참조해야합니다.

한 곳에 모든 방법을 갖고 싶다면 모든 방법을 교체 클래스에 항상 추가 할 수 있습니다.

그래서 당신은 같은 것을 가질 것입니다

using FooConsole = System.Console;

public static class Console
{
    public static void WriteBlueLine(string text)
    {
        FooConsole.ForegroundColor = ConsoleColor.Blue;
        FooConsole.WriteLine(text);
        FooConsole.ResetColor();
    }
    public static void WriteLine(string text)
    {
        FooConsole.WriteLine(text);
    }
...etc.
}

이것은 당신이 찾고있는 행동의 종류를 제공 할 것입니다.

*참고 콘솔은 네임 스페이스를 통해 추가해야합니다.

NULL의 캐스트를 사용하여 작동하게 할 수 있습니다.

public static class YoutTypeExtensionExample
{
    public static void Example()
    {
        ((YourType)null).ExtensionMethod();
    }
}

확장 :

public static class YourTypeExtension
{
    public static void ExtensionMethod(this YourType x) { }
}

YourType :

public class YourType { }

정적 클래스의 변수를 만들어 NULL에 할당하여 조금 "프리그"하려면이 작업을 수행 할 수 있습니다. 그러나이 방법은 클래스에서 정적 호출에 사용할 수 없으므로 얼마나 많은 사용이 있는지 확실하지 않습니다.

Console myConsole = null;
myConsole.WriteBlueLine("my blue line");

public static class Helpers {
    public static void WriteBlueLine(this Console c, string text)
    {
        Console.ForegroundColor = ConsoleColor.Blue;
        Console.WriteLine(text);
        Console.ResetColor();
    }
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top