Can anonymous class implement interface?
Asked Answered
N

9

514

Is it possible to have an anonymous type implement an interface?

I've got a piece of code that I would like to work, but don't know how to do this.

I've had a couple of answers that either say no, or create a class that implements the interface construct new instances of that. This isn't really ideal, but I'm wondering if there is a mechanism to create a thin dynamic class on top of an interface which would make this simple.

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

I've found an article Dynamic interface wrapping that describes one approach. Is this the best way of doing this?

Nephro answered 10/10, 2008 at 12:20 Comment(4)
Link appears out of date, this maybe a suitable alternative liensberger.it/web/blog/?p=298.Ambrogino
Yes, you can do this with .NET 4 and higher (via the DLR), using the ImpromptuInterface nuget package.Legere
@PhilCooper Your link has been down, probably since at least 2016 - but luckily it was archived before then. web.archive.org/web/20111105150920/http://www.liensberger.it/…Chlorobenzene
in C#9 you can use records (devblogs.microsoft.com/dotnet/welcome-to-c-9-0) I know this is not the same but also a very lightweight approach to implement "simple" interfaces :)Binominal
H
392

No, anonymous types cannot implement an interface. From the C# programming guide:

Anonymous types are class types that consist of one or more public read-only properties. No other kinds of class members such as methods or events are allowed. An anonymous type cannot be cast to any interface or type except for object.

Hook answered 10/10, 2008 at 12:30 Comment(7)
would be nice to have this stuff anyway. If you're talking code readability, lambda expressions are usually not the way to go. If we're talking RAD, I'm all into java-like anonymous interface implementation. By the way, in some cases that feature is more powerful than delegatesTypical
@ArsenZahray: lambda expressions, when used well, actually increase code readability. They're particularly powerful when used in functional chains, which can reduce or eliminate the need for local variables.Villiers
You could do the trick this way "Anonymous Implementation Classes – A Design Pattern for C#" - twistedoakstudios.com/blog/…Zimbabwe
@DmitryPavlov, that was surprisingly valuable. Passersby: here is the condensed version.Slotnick
you can cast the anonymous type to an anonymous object with the same fields https://mcmap.net/q/75108/-cast-to-anonymous-typeUpend
This answer is not entirely correct. -- This can be done in .NET 4.0 and higher using the DLR, it just takes a bit of glue and magic to make it all work. -- Luckily, there's a package called ImpromptuInterface that makes it really easy to do, and it lets you retroactively cast any object (even anonymous typed objects) to any interface (of course there will be late binding exceptions if you try to use a part of the interface that the class doesn't actually support).Legere
Anonymous types are enormously useful in JSON serialisation when C# is used as a backbone for ajax calls and dynamic objects need to be created on the fly. (90% of the server code in some applications).Biogen
C
99

While the answers in the thread are all true enough, I cannot resist the urge to tell you that it in fact is possible to have an anonymous class implement an interface, even though it takes a bit of creative cheating to get there.

Back in 2008 I was writing a custom LINQ provider for my then employer, and at one point I needed to be able to tell "my" anonymous classes from other anonymous ones, which meant having them implement an interface that I could use to type check them. The way we solved it was by using aspects (we used PostSharp), to add the interface implementation directly in the IL. So, in fact, letting anonymous classes implement interfaces is doable, you just need to bend the rules slightly to get there.

Cruiser answered 17/9, 2010 at 11:7 Comment(1)
It's a lot easier now; no need to modify the IL after the code is generated; just use ImpromptuInterface. -- It lets you bind any object (including anonymously typed objects) to any interface (of course there will be late binding exceptions if you try to use a part of the interface that the class doesn't actually support).Legere
E
49

Casting anonymous types to interfaces has been something I've wanted for a while but unfortunately the current implementation forces you to have an implementation of that interface.

The best solution around it is having some type of dynamic proxy that creates the implementation for you. Using the excellent LinFu project you can replace

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

with

 select new DynamicObject(new
 {
   A = value.A,
   B = value.C + "_" + value.D
 }).CreateDuck<DummyInterface>();
Enginery answered 3/11, 2008 at 6:32 Comment(3)
Impromptu-Interface Project will do this in .NET 4.0 using the DLR and is lighter weight then Linfu.Assyria
Is DynamicObject a LinFu type? System.Dynamic.DynamicObject only has a protected constructor (at least in .NET 4.5).Loan
Yes. I was referring to the LinFu implementation of DynamicObject which predates the DLR versionEnginery
L
17

Anonymous types can implement interfaces via a dynamic proxy.

I wrote an extension method on GitHub and a blog post http://wblo.gs/feE to support this scenario.

The method can be used like this:

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; }
}
Lamee answered 9/11, 2014 at 5:8 Comment(0)
L
13

No; an anonymous type can't be made to do anything except have a few properties. You will need to create your own type. I didn't read the linked article in depth, but it looks like it uses Reflection.Emit to create new types on the fly; but if you limit discussion to things within C# itself you can't do what you want.

Lichenology answered 10/10, 2008 at 12:25 Comment(3)
And important to note: properties may include functions or voids (Action) as well: select new { ... MyFunction = new Func<string,bool>(s => value.A == s) } works though you cannot refer to new properties in your functions (we can't use "A" in lieu of "value.A").Pareu
Well, isn't that just a property that happens to be a delegate? It isn't actually a method.Lichenology
I've used Reflection.Emit to create types at runtime but believe now I would prefer an AOP solution to avoid the runtime costs.Playa
Y
10

The best solution is just not to use anonymous classes.

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

Note that you need to cast the result of the query to the type of the interface. There might be a better way to do it, but I couldn't find it.

Yunyunfei answered 10/10, 2008 at 12:55 Comment(1)
You could use values.OfType<IDummyInterface>() instead of cast. It only returns the objects in your collection that actually can be cast to that type. It all depends on what you want.Fullfaced
S
9

The answer to the question specifically asked is no. But have you been looking at mocking frameworks? I use MOQ but there's millions of them out there and they allow you to implement/stub (partially or fully) interfaces in-line. Eg.

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);
}
Simba answered 18/7, 2014 at 10:58 Comment(0)
R
2

Another option is to create a single, concrete implementing class that takes lambdas in the constructor.

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

If all you are ever going to do is convert DummySource to DummyInterface, then it would be simpler to just have one class that takes a DummySource in the constructor and implements the interface.

But, if you need to convert many types to DummyInterface, this is much less boiler plate.

Recursion answered 10/4, 2019 at 21:44 Comment(0)
M
0

Using Roslyn, you can dynamically create a class which inherits from an interface (or abstract class).

I use the following to create concrete classes from abstract classes.

In this example, AAnimal is an abstract class.

var personClass = typeof(AAnimal).CreateSubclass("Person");

Then you can instantiate some objects:

var person1 = Activator.CreateInstance(personClass);
var person2 = Activator.CreateInstance(personClass);

Without a doubt this won't work for every case, but it should be enough to get you started:

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;

namespace Publisher
{
    public static class Extensions
    {
        public static Type CreateSubclass(this Type baseType, string newClassName, string newNamespace = "Magic")
        {
            //todo: handle ref, out etc.
            var concreteMethods = baseType
                                    .GetMethods()
                                    .Where(method => method.IsAbstract)
                                    .Select(method =>
                                    {
                                        var parameters = method
                                                            .GetParameters()
                                                            .Select(param => $"{param.ParameterType.FullName} {param.Name}")
                                                            .ToString(", ");

                                        var returnTypeStr = method.ReturnParameter.ParameterType.Name;
                                        if (returnTypeStr.Equals("Void")) returnTypeStr = "void";

                                        var methodString = @$"
                                        public override {returnTypeStr} {method.Name}({parameters})
                                        {{
                                            Console.WriteLine(""{newNamespace}.{newClassName}.{method.Name}() was called"");
                                        }}";

                                        return methodString.Trim();
                                    })
                                    .ToList();

            var concreteMethodsString = concreteMethods
                                        .ToString(Environment.NewLine + Environment.NewLine);

            var classCode = @$"
            using System;

            namespace {newNamespace}
            {{
                public class {newClassName}: {baseType.FullName}
                {{
                    public {newClassName}()
                    {{
                    }}

                    {concreteMethodsString}
                }}
            }}
            ".Trim();

            classCode = FormatUsingRoslyn(classCode);


            /*
            var assemblies = new[]
            {
                MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
                MetadataReference.CreateFromFile(baseType.Assembly.Location),
            };
            */

            var assemblies = AppDomain
                        .CurrentDomain
                        .GetAssemblies()
                        .Where(a => !string.IsNullOrEmpty(a.Location))
                        .Select(a => MetadataReference.CreateFromFile(a.Location))
                        .ToArray();

            var syntaxTree = CSharpSyntaxTree.ParseText(classCode);

            var compilation = CSharpCompilation
                                .Create(newNamespace)
                                .AddSyntaxTrees(syntaxTree)
                                .AddReferences(assemblies)
                                .WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));

            using (var ms = new MemoryStream())
            {
                var result = compilation.Emit(ms);
                //compilation.Emit($"C:\\Temp\\{newNamespace}.dll");

                if (result.Success)
                {
                    ms.Seek(0, SeekOrigin.Begin);
                    Assembly assembly = Assembly.Load(ms.ToArray());

                    var newTypeFullName = $"{newNamespace}.{newClassName}";

                    var type = assembly.GetType(newTypeFullName);
                    return type;
                }
                else
                {
                    IEnumerable<Diagnostic> failures = result.Diagnostics.Where(diagnostic =>
                        diagnostic.IsWarningAsError ||
                        diagnostic.Severity == DiagnosticSeverity.Error);

                    foreach (Diagnostic diagnostic in failures)
                    {
                        Console.Error.WriteLine("{0}: {1}", diagnostic.Id, diagnostic.GetMessage());
                    }

                    return null;
                }
            }
        }

        public static string ToString(this IEnumerable<string> list, string separator)
        {
            string result = string.Join(separator, list);
            return result;
        }

        public static string FormatUsingRoslyn(string csCode)
        {
            var tree = CSharpSyntaxTree.ParseText(csCode);
            var root = tree.GetRoot().NormalizeWhitespace();
            var result = root.ToFullString();
            return result;
        }
    }
}
Mirnamirror answered 12/9, 2020 at 13:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.