익명 클래스가 인터페이스를 구현할 수 있습니까?


463

익명 유형으로 인터페이스를 구현할 수 있습니까?

작동하고 싶은 코드가 있지만이 작업을 수행하는 방법을 모르겠습니다.

아니오라고 말하거나 인터페이스를 구현하는 클래스를 작성하여 새로운 인스턴스를 생성하는 몇 가지 답변이 있습니다. 이것은 이상적이지는 않지만 인터페이스 위에 얇은 동적 클래스를 만들어서 간단하게 만들 수있는 메커니즘이 있는지 궁금합니다.

public interface DummyInterface
{
    string A { get; }
    string B { get; }
}

public class DummySource
{
    public string A { get; set; }
    public string C { get; set; }
    public string D { get; set; }
}

public class Test
{
    public void WillThisWork()
    {
        var source = new DummySource[0];
        var values = from value in source
                     select new
                     {
                         A = value.A,
                         B = value.C + "_" + value.D
                     };

        DoSomethingWithDummyInterface(values);

    }

    public void DoSomethingWithDummyInterface(IEnumerable<DummyInterface> values)
    {
        foreach (var value in values)
        {
            Console.WriteLine("A = '{0}', B = '{1}'", value.A, value.B);
        }
    }
}

한 가지 접근 방식을 설명하는 동적 인터페이스 줄 바꿈 기사를 찾았습니다 . 이것이 최선의 방법입니까?


1
링크가 최신 상태가 아닌 것으로 보이며 , 적절한 대안 liensberger.it/web/blog/?p=298 일 수 있습니다.
Phil Cooper

1
예, ImpromptuInterface nuget 패키지를 사용하여 .NET 4 이상 (DLR을 통해)으로이 작업을 수행 할 수 있습니다 .
BrainSlugs83

1
@PhilCooper 적어도 2016 년 이후 링크가 다운되었을 것입니다. 그러나 다행히도 그 전에는 보관되었습니다. web.archive.org/web/20111105150920/http://www.liensberger.it/…
Paul

답변:


361

아니요, 익명 유형은 인터페이스를 구현할 수 없습니다. 로부터 C # 프로그래밍 가이드 :

익명 형식은 하나 이상의 공용 읽기 전용 속성으로 구성된 클래스 형식입니다. 메서드 나 이벤트와 같은 다른 종류의 클래스 멤버는 허용되지 않습니다. 익명 유형은 객체를 제외한 모든 인터페이스 또는 유형으로 캐스트 할 수 없습니다.


5
어쨌든이 물건을 갖는 것이 좋을 것입니다. 코드 가독성을 말하는 경우 람다 식은 일반적으로 갈 길이 아닙니다. 우리가 RAD를 이야기하고 있다면, 나는 모두 자바와 같은 익명 인터페이스 구현에 빠져 있습니다. 그건 그렇고, 어떤 경우에는 그 기능이 대리인보다 강력합니다
Arsen Zahray

18
@ArsenZahray : 람다 식은 잘 사용될 때 실제로 코드 가독성을 증가시킵니다. 기능 체인에서 사용될 때 특히 강력하므로 지역 변수의 필요성을 줄이거 나 없앨 수 있습니다.
Roy Tinker

2
"익명 구현 클래스 – C #을위한 디자인 패턴" -twistedoakstudios.com/blog/…
Dmitry Pavlov

3
@DmitryPavlov, 놀랍게도 귀중했습니다. 행인 : 여기에 응축 된 버전입니다.
kdbanman

1
동일한 필드를 가진 익명 객체에 익명 유형을 캐스트 할 수 있습니다. stackoverflow.com/questions/1409734/cast-to-anonymous-type
Zinov

89

이것은 2 살짜리 질문 일지 모르지만 스레드의 대답은 모두 사실이지만 익명의 클래스가 인터페이스를 구현하는 것이 가능 하다는 사실을 말하려는 충동에 저항 할 수는 없습니다 . 거기에 도착하는 창의적인 속임수.

2008 년에 나는 당시 고용주를 위해 커스텀 LINQ 공급자를 작성하고 있었고, 언젠가는 다른 익명의 사람들로부터 "나의"익명 클래스를 말할 수 있어야했습니다. 그들. 우리가 해결 한 방법은 aspect ( PostSharp 사용 )를 사용 하여 인터페이스 구현을 IL에 직접 추가하는 것입니다. 사실, 익명 클래스가 인터페이스를 구현 하게하는 것은 가능합니다. 규칙을 약간 구부려 야합니다.


8
이 경우 @Gusdor는 빌드를 완벽하게 제어했으며 항상 전용 시스템에서 실행되었습니다. 또한 PostSharp를 사용하고 있었기 때문에 해당 프레임 워크 내에서 완벽하게 합법적이므로 PostSharp가 사용중인 빌드 서버에 설치되어 있는지 확인하는 한 아무 것도 나타나지 않습니다.
Mia Clarke

16
@Gusdor 저는 다른 프로그래머들이 프로젝트를 쉽게 수행하고 많은 어려움없이 컴파일하는 것이 쉬워야한다는 데 동의하지만, postsharp과 같은 툴링이나 프레임 워크를 완전히 피하지 않으면 서 개별적으로 해결할 수있는 다른 문제입니다. VS 자체 또는 C # 사양의 일부가 아닌 다른 비표준 MS 프레임 워크에 대해 동일한 주장을 할 수 있습니다. 당신은 그런 것들이 필요하거나 그렇지 않으면 "튀어 나옵니다". 그것은 문제 IMO가 아닙니다. 문제는 빌드가 너무 복잡 해져서 모든 것이 제대로 작동하기가 어렵다는 것입니다.
AaronLS

6
@ZainRizvi 아니, 그렇지 않았다. 내가 아는 한, 여전히 생산 중입니다. 제기 된 우려는 그때 나에게 이상해 보였고 지금은 자의적이었다. 기본적으로 "프레임 워크를 사용하지 마십시오. 문제가 발생합니다!"입니다. 그들은 아무것도 아니었다. 그리고 나는 놀라지 않았다.
Mia Clarke

3
훨씬 쉬워졌습니다. 코드가 생성 된 후 IL을 수정할 필요가 없습니다. ImpromptuInterface 만 사용하십시오 . -익명으로 입력 된 객체를 포함한 모든 객체를 인터페이스에 바인딩 할 수 있습니다 (물론 클래스가 실제로 지원하지 않는 인터페이스의 일부를 사용하려고하면 바인딩 바인딩이 늦어 질 수 있습니다).
BrainSlugs83

44

익명 유형을 인터페이스에 캐스트하는 것은 한동안 원했던 것이었지만 불행히도 현재 구현에서는 해당 인터페이스를 구현해야합니다.

가장 좋은 해결책은 구현을 만드는 일종의 동적 프록시를 사용하는 것입니다. 우수한 LinFu 프로젝트 를 사용하면 교체 할 수 있습니다

select new
{
  A = value.A,
  B = value.C + "_" + value.D
};

 select new DynamicObject(new
 {
   A = value.A,
   B = value.C + "_" + value.D
 }).CreateDuck<DummyInterface>();

17
Impromptu-Interface Project 는 DLR을 사용하여 .NET 4.0에서이 작업을 수행하며 Linfu보다 가볍습니다.
jbtule

DynamicObjectLinFu 유형은? System.Dynamic.DynamicObject적어도 .NET 4.5에서는 보호 된 생성자 만 있습니다.
jdmcnair

예. 나는의 LinFu 구현에 참조 된 DynamicObjectDLR에 버전을 선행하는
아르네 Claassen

15

익명 유형은 동적 프록시를 통해 인터페이스를 구현할 수 있습니다.

이 시나리오를 지원하기 위해 GitHub에 확장 방법 과 블로그 게시물 http://wblo.gs/feE 를 작성했습니다 .

이 방법은 다음과 같이 사용할 수 있습니다.

class Program
{
    static void Main(string[] args)
    {
        var developer = new { Name = "Jason Bowers" };

        PrintDeveloperName(developer.DuckCast<IDeveloper>());

        Console.ReadKey();
    }

    private static void PrintDeveloperName(IDeveloper developer)
    {
        Console.WriteLine(developer.Name);
    }
}

public interface IDeveloper
{
    string Name { get; }
}

13

아니; 익명 유형은 몇 가지 속성을 제외하고는 아무것도 할 수 없습니다. 자신 만의 유형을 만들어야합니다. 링크 된 기사를 자세히 읽지는 않았지만 Reflection.Emit을 사용하여 새 유형을 즉석에서 만드는 것처럼 보입니다. 그러나 C # 자체의 내용으로 토론을 제한 하면 원하는 것을 수행 할 수 없습니다.


주의해야 할 중요한 점 : 속성에는 함수 또는 void (Action)도 포함될 수 있습니다. select new {... MyFunction = new Func <string, bool> (s => value.A == s)} 함수의 새로운 속성 ( 'value.A'대신 'A'를 사용할 수 없음)
cfeduke

2
글쎄, 그건 단지 대리인이되는 재산이 아닌가? 실제로는 방법이 아닙니다.
Marc Gravell

1
Reflection.Emit을 사용하여 런타임에 유형을 만들었지 만 이제는 런타임 비용을 피하기 위해 AOP 솔루션을 선호한다고 생각합니다.
Norman H

11

가장 좋은 해결책은 익명 클래스를 사용하지 않는 것입니다.

public class Test
{
    class DummyInterfaceImplementor : IDummyInterface
    {
        public string A { get; set; }
        public string B { get; set; }
    }

    public void WillThisWork()
    {
        var source = new DummySource[0];
        var values = from value in source
                     select new DummyInterfaceImplementor()
                     {
                         A = value.A,
                         B = value.C + "_" + value.D
                     };

        DoSomethingWithDummyInterface(values.Cast<IDummyInterface>());

    }

    public void DoSomethingWithDummyInterface(IEnumerable<IDummyInterface> values)
    {
        foreach (var value in values)
        {
            Console.WriteLine("A = '{0}', B = '{1}'", value.A, value.B);
        }
    }
}

조회 결과를 인터페이스 유형으로 캐스트해야합니다. 더 좋은 방법이 있지만 찾을 수 없었습니다.


2
values.OfType<IDummyInterface>()캐스트 대신 사용할 수 있습니다 . 실제로 해당 유형으로 캐스트 할 수있는 컬렉션의 객체 만 반환합니다. 그것은 모두 당신이 원하는 것에 달려 있습니다.
Kristoffer L

7

구체적으로 묻는 질문에 대한 대답은 '아니요'입니다. 그러나 모의 프레임 워크를보고 있습니까? 나는 MOQ를 사용하지만 수백만 개가 있으며 인라인으로 인터페이스를 (부분적으로 또는 완전히) 구현 / 스텁 할 수 있습니다. 예 :

public void ThisWillWork()
{
    var source = new DummySource[0];
    var mock = new Mock<DummyInterface>();

    mock.SetupProperty(m => m.A, source.Select(s => s.A));
    mock.SetupProperty(m => m.B, source.Select(s => s.C + "_" + s.D));

    DoSomethingWithDummyInterface(mock.Object);
}

1

또 다른 옵션은 생성자에서 람다를 취하는 하나의 구체적 구현 클래스를 만드는 것입니다.

public interface DummyInterface
{
    string A { get; }
    string B { get; }
}

// "Generic" implementing class
public class Dummy : DummyInterface
{
    private readonly Func<string> _getA;
    private readonly Func<string> _getB;

    public Dummy(Func<string> getA, Func<string> getB)
    {
        _getA = getA;
        _getB = getB;
    }

    public string A => _getA();

    public string B => _getB();
}

public class DummySource
{
    public string A { get; set; }
    public string C { get; set; }
    public string D { get; set; }
}

public class Test
{
    public void WillThisWork()
    {
        var source = new DummySource[0];
        var values = from value in source
                     select new Dummy // Syntax changes slightly
                     (
                         getA: () => value.A,
                         getB: () => value.C + "_" + value.D
                     );

        DoSomethingWithDummyInterface(values);

    }

    public void DoSomethingWithDummyInterface(IEnumerable<DummyInterface> values)
    {
        foreach (var value in values)
        {
            Console.WriteLine("A = '{0}', B = '{1}'", value.A, value.B);
        }
    }
}

당신이 이제까지 할 모든 변환 인 경우 DummySourceDummyInterface, 단지 소요 하나 개의 클래스가하는 것이 더 간단 할 것이다 DummySource생성자 및 구현하는 인터페이스입니다.

그러나 많은 유형을로 변환 해야하는 경우 DummyInterface보일러 판이 훨씬 적습니다.

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