C #에서 확장 메서드로 클래스 메서드를 재정의하는 방법이 있습니까?


98

확장 메서드로 클래스의 메서드를 재정의하려는 경우가있었습니다. C #에서 할 수있는 방법이 있습니까?

예를 들면 :

public static class StringExtension
{
    public static int GetHashCode(this string inStr)
    {
        return MyHash(inStr);
    }
}

이 작업을 수행하고 싶었던 경우는 문자열의 해시를 데이터베이스에 저장하고 문자열 클래스의 해시 (예 : 사전 등)를 사용하는 모든 클래스에서 동일한 값을 사용할 수 있도록하는 것입니다. 기본 제공 .Net 해싱 알고리즘은 프레임 워크의 한 버전에서 다음 버전으로의 호환성이 보장되지 않습니다.이 알고리즘을 내 것으로 교체하고 싶습니다.

확장 메서드로 클래스 메서드를 재정의하려는 다른 경우도 있으므로 문자열 클래스 또는 GetHashCode 메서드에만 국한되지 않습니다.

기존 클래스에서 서브 클래 싱하여이 작업을 수행 할 수 있다는 것을 알고 있지만 많은 경우 확장 기능을 사용하여 수행 할 수 있으면 편리합니다.


사전은 메모리 내 데이터 구조이므로 해싱 알고리즘이 프레임 워크 버전에서 다음 버전으로 변경되는지 여부는 어떤 차이가 있습니까? 프레임 워크 버전이 변경되면 분명히 애플리케이션이 다시 시작되고 사전이 다시 빌드 된 것입니다.
David Nelson

답변:


92

아니; 확장 방법은 다형성에 참여 (결코 적합한 서명 인스턴스 메소드에 우선하지 않으며 결코 GetHashCodeA는 virtual방법).


"... 절대 다형성에 참여하지 않습니다 (GetHashCode는 가상 메소드입니다)"확장 메소드가 가상이기 때문에 다형성에 참여하지 않는 다른 방법으로 설명해 주시겠습니까? 이 두 진술과의 연결을 보는 데 문제가 있습니다.
Alex

3
@Alex를 언급함으로써 나는 단순히 다형성이라는 것이 무엇을 의미하는지 명확히하고 있습니다. 거의 모든 GetHashCode 사용에서 구체적인 유형은 알 수 없으므로 다형성이 작용합니다. 따라서 확장 방법은 도움이되지 않습니다. 일반 컴파일러에서 우선 순위차지하더라도 . OP가 정말로 원하는 것은 원숭이 패치입니다. C # 및 .net이 용이하지 않습니다.
Marc Gravell

4
슬픈 일입니다. C #에서는 예를 들어 .ToString()배열 과 같이 오해의 소지가있는 말도 안되는 메서드가 너무 많습니다 . 확실히 필요한 기능입니다.
하이 엔젤

그러면 컴파일러 오류가 발생하지 않는 이유는 무엇입니까? 예 : 유형. namespace System { public static class guilty { public static string ToLower(this string s) { return s.ToUpper() + " I'm Malicious"; } } }
LowLevel

@LowLevel 왜 그럴까요?
Marc Gravell

7

메서드에 다른 서명이 있으면 수행 할 수 있습니다. 따라서 귀하의 경우 : 아니요.

그러나 그렇지 않으면 원하는 것을 수행하기 위해 상속을 사용해야합니다.


2

내가 아는 한 대답은 '아니오'입니다. 왜냐하면 확장 메서드는 인스턴스가 아니기 때문입니다. 클래스의 인스턴스를 사용하여 정적 메서드를 호출 할 수있게 해주는 인텔리 센스 기능에 가깝습니다. 문제에 대한 해결책은 특정 메서드 (예 : GetHashCode ())의 실행을 가로 채고 다른 작업을 수행하는 인터셉터가 될 수 있다고 생각합니다. 이러한 인터셉터를 사용하려면 (Castle Project가 제공하는 것과 같은) 모든 개체는 다음을 사용하여 인스턴스화되어야합니다. 개체 팩토리 (또는 Castle의 IoC 컨테이너)를 사용하여 런타임에 생성 된 동적 프록시를 통해 인터페이스를 가로 챌 수 있습니다 (Caslte를 사용하면 클래스의 가상 구성원도 가로 챌 수 있음).


0

클래스 메서드와 동일한 서명을 사용하여 확장 메서드를 호출하는 방법을 찾았지만 그다지 우아하지는 않습니다. 확장 메서드를 가지고 놀 때 문서화되지 않은 동작을 발견했습니다. 샘플 코드 :

public static class TestableExtensions
{
    public static string GetDesc(this ITestable ele)
    {
        return "Extension GetDesc";
    }

    public static void ValDesc(this ITestable ele, string choice)
    {
        if (choice == "ext def")
        {
            Console.WriteLine($"Base.Ext.Ext.GetDesc: {ele.GetDesc()}");
        }
        else if (choice == "ext base" && ele is BaseTest b)
        {
            Console.WriteLine($"Base.Ext.Base.GetDesc: {b.BaseFunc()}");
        }
    }

    public static string ExtFunc(this ITestable ele)
    {
        return ele.GetDesc();
    }

    public static void ExtAction(this ITestable ele, string choice)
    {
        ele.ValDesc(choice);
    }
}

public interface ITestable
{

}

public class BaseTest : ITestable
{
    public string GetDesc()
    {
        return "Base GetDesc";
    }

    public void ValDesc(string choice)
    {
        if (choice == "")
        {
            Console.WriteLine($"Base.GetDesc: {GetDesc()}");
        }
        else if (choice == "ext")
        {
            Console.WriteLine($"Base.Ext.GetDesc: {this.ExtFunc()}");
        }
        else
        {
            this.ExtAction(choice);
        }
    }

    public string BaseFunc()
    {
        return GetDesc();
    }
}

내가 알아 차린 것은 확장 메서드 내부에서 두 번째 메서드를 호출하면 서명과 일치하는 클래스 메서드가 있어도 서명과 일치하는 확장 메서드를 호출한다는 것입니다. 예를 들어 위 코드에서 ExtFunc ()를 호출하면 ele.GetDesc ()를 호출 할 때 예상되는 문자열 "Base GetDesc"대신 반환 문자열 "Extension GetDesc"를 얻습니다.

코드 테스트 :

var bt = new BaseTest();
bt.ValDesc("");
//Output is Base.GetDesc: Base GetDesc
bt.ValDesc("ext");
//Output is Base.Ext.GetDesc: Extension GetDesc
bt.ValDesc("ext def");
//Output is Base.Ext.Ext.GetDesc: Extension GetDesc
bt.ValDesc("ext base");
//Output is Base.Ext.Base.GetDesc: Base GetDesc

이렇게하면 원하는대로 클래스 메서드와 확장 메서드간에 앞뒤로 바운스 할 수 있지만 원하는 "범위"에 도달하려면 중복 "통과"메서드를 추가해야합니다. 더 나은 단어가 없기 때문에 여기에서 범위라고 부릅니다. 누군가가 실제로 그것이 무엇인지 알려줄 수 있기를 바랍니다.

하나 또는 두 개의 메서드가 동일한 시그니처를 가진 여러 메서드에 대한 통과 역할을 할 수 있기를 바라면서 대리자를 전달하는 아이디어도 가지고 놀았 던 "통과"메서드 이름을 추측했을 것입니다. 불행히도 델리게이트의 압축을 풀면 다른 확장 메서드 내부에서도 항상 확장 메서드 대신 클래스 메서드를 선택했습니다. "범위"는 더 이상 중요하지 않습니다. 나는 Action 및 Func 델리게이트를 많이 사용하지 않았으므로 더 경험이 많은 누군가가 그 부분을 알아낼 수있을 것입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.