0

I'm trying to convert some anonymous type back to its original strong type class.

I have some legacy code (which I cannot touch) which create an anonymous class:


public class Cat : FooId
{
    public int Id { get; set; }
    public string Name { get; set; }
}

var result = new
{
    Id = Mapper.Map<TFooId>(someCat)
};

NOTE: I've tried to make this fake class and interface similar to my code.

This then gives me:

  • result.GetType().ToString() : <>f__AnonymousType1``1[MyProject.Cat]

From here, I'm not sure how to convert this back to a MyProject.Cat instance?

I've tried (and fails):

  • (MyProject.Cat)result
  • (dynamic)result

but both fail. The dynamic doesn't throw an error ... but I can't access any properties in it.

halfer
  • 19,824
  • 17
  • 99
  • 186
Pure.Krome
  • 84,693
  • 113
  • 396
  • 647
  • 1
    and what is a type of `result.Id` ? prolly `TFooId` but what it is ? a `Cat` ? and what is `someCat` instance of `Cat` ? – Selvin Aug 23 '21 at 15:06
  • if you wana create `Cat` from anonymouse class like `var cat = Mapper.Map(new { Id=1, Name="Furry"});` then unfortunately [in current AutoMapper you have to use reflection](https://stackoverflow.com/questions/9639451/how-to-map-an-anonymous-object-to-a-class-by-automapper) (iterate over `Cat` 's props and pair them with anonymouse class and configure maps) – Selvin Aug 23 '21 at 15:27

2 Answers2

2

C# is a statically typed language, and those two types are not in any way related to one another. Unless you're able to modify the code which defines those types, the way you'd convert from one to the other would be to create a new instance of the target type and populate it from the source object.

For example:

var resultCat = new Cat { Id = result.Id };

Edit: From comments it looks like it may be possible that the Id property on the result object may be an instance of Cat or some other object? You're going to need to do some debugging to find out what your types are.

But the overall concept doesn't really change. If you have an instance of Cat in your results then you can use that instance. If you don't then in order to create one you'd need to create a new instance and populate it with the data you have. Even if two types are intuitively or semantically similar, they are different types.

David
  • 208,112
  • 36
  • 198
  • 279
  • isn't `result.Id` a `Cat` ? – Selvin Aug 23 '21 at 15:09
  • @Selvin: Is it? I suppose you may be right. Which would make this code even stranger than it already is. It sounds like whoever wrote the original legacy code may have been pretty confused, and the OP is left with some technical debt for which there is no real shortcut around. – David Aug 23 '21 at 15:12
  • Yeah - it's not nice. but that is correct @Selvin .. `result.Id` is a `Cat` instance. – Pure.Krome Aug 23 '21 at 16:48
0

It's true what David said with regard to the fact that C# is a statically-typed language and that the new instance should be populated from the source the way he suggested.

However, there are work-arounds (though less performant) for that, such as reflection.

Consider you have a console app where you have defined ObjectExtensions as follows:

public static class ObjectExtensions
{
    public static TOut Map<TOut>(this object @in)
        where TOut : new()
    {
        TOut @out = new TOut();

        if (@in?.GetType() is Type tin)
        {
            Type tout = typeof(TOut);

            foreach ((PropertyInfo pout, PropertyInfo pin) in tout.GetProperties().Join(tin.GetProperties(), pi => pi.Name, pi => pi.Name, (pout, pin) => (pout, pin)))
            {
                pout.SetValue(@out, pin.GetValue(@in));
            }
        }

        return @out;
    }
}

And Class1 as follows:

public class Class1
{
    public string A { get; set; } = "A";
    public string B { get; set; } = "B";
    public string C { get; set; } = "C";

    public override string ToString()
    {
        return $"{{A={A}, B={B}, C={C}}}";
    }
}

You will be able to map your anonymous type back to its original strongly-typed class like this:

Console.WriteLine(new { A = "Anonymous A", B = "Anonymous B", C = "Anonymous C" }.Map<Class1>());

Therefore the bloc above should show the following output:

{A=Anonymous A, B=Anonymous B, C=Anonymous C}

In this case, of course, I have assumed that Class1 (Cat in your example) must have a public parameterless constructor. That may not always be the case. There are more sophisticated scenarios of course that might involve other techniques for creating the object such as cloning or dependency injection. Just saying that the idea of yours is possible.

Aly Elhaddad
  • 1,913
  • 1
  • 15
  • 31