3

I have an interface for automapper. And DTOs implement this interfaces. As you can see there is a default method.

public interface IMap<T> {
    public void Mapping(Profile profile) {
        profile.CreateMap(typeof(T), GetType()).ReverseMap();
    }
}

public class ItemDto : IMap<Item> {
    public string Name { get; set; }
}

When I try to invoke this method. The method cannot be found.

public class MappingProfile : Profile {
    public MappingProfile() {
        ApplyMappingsFromAssembly();
    }

    private void ApplyMappingsFromAssembly() {
        var types = AppDomain.CurrentDomain.GetAssemblies().Where(w => !w.IsDynamic).SelectMany(s => s.GetExportedTypes())
            .Where(t => t.GetInterfaces().Any(i =>
                i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IMap<>)))
            .ToList();

        foreach (var type in types) {
            var instance = Activator.CreateInstance(type);
            var methodInfo = type.GetMethod("Mapping");
            //In here I expect to call default interface method.
            methodInfo?.Invoke(instance, new object[] { this });
        }
    }
}

How can I call default interface method?

Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
is_oz
  • 813
  • 1
  • 8
  • 27
  • Does this answer your question? [C# 8 base interface's default method invocation workaround](https://stackoverflow.com/questions/59398027/c-sharp-8-base-interfaces-default-method-invocation-workaround) – Sinatr Mar 10 '21 at 12:19
  • Why not use the Interface as marker, only and use an ExtensionMethod? – Fildor Mar 10 '21 at 12:26
  • @Sinatr Thanks, there no reflection there and I dont want to add empty method just calling to base. – is_oz Mar 10 '21 at 12:28
  • @Fildor sometimes I need to override defaults. So I made interface. – is_oz Mar 10 '21 at 12:29
  • 1
    So? How is that stopping you from using extensionmethods? – Fildor Mar 10 '21 at 12:30
  • @is_oz DIMs work the same way as explicit interface implementations, so you *have* to either cast to the interface or add an extra method just to call it without a cast. A DIM isn't a replacement for abstract base classes – Panagiotis Kanavos Mar 10 '21 at 12:31
  • When I implement an interface, I do not add anything to anywhere again. Because it is default. When I use extention method, I need to call several time in my scenario. May be I do not understand clearly what you suggest. – is_oz Mar 10 '21 at 12:32
  • 4
    You don't understand how DIMs work. You need to add something extra *everywhere*. You can't call `Mapping()` or even `base.Mapping` from inside `ItemDto`. You have to *cast* to `IMap`, ie `((IMap)this).Mapping()`. From the outside, the only way to call `Mapping` is through the `IMap` interface, either through a cast or by using `IMap` parameters and values. BTW that's how explicit interface implementations work too – Panagiotis Kanavos Mar 10 '21 at 12:35
  • 2
    In this case, the name of the method is `IMap.Mapping` not just `Mapping` – Panagiotis Kanavos Mar 10 '21 at 12:38
  • 1
    Does this answer your question? [How can I access an explicitly implemented method using reflection?](https://stackoverflow.com/questions/3650575/how-can-i-access-an-explicitly-implemented-method-using-reflection) – Panagiotis Kanavos Mar 10 '21 at 12:39
  • @PanagiotisKanavos thanks I understand why is not happen. I will change my code. – is_oz Mar 10 '21 at 12:44
  • @Fildor thanks showcase, I think I handle it without extention. – is_oz Mar 10 '21 at 12:45
  • ^^ Improved: https://dotnetfiddle.net/BKNUsg , but it's your decision. – Fildor Mar 10 '21 at 12:46

1 Answers1

1

You need to invoke the method against the interface, that includes getting the method with reflection too. For example:

// Create the IMap<Item> type
var mapType = typeof(IMap<>).MakeGenericType(typeof(Item));

// Create the instance as you did before
var instance = Activator.CreateInstance(typeof(ItemDto));

// Get the method from the interface
var method = mapType.GetMethod("Mapping");

// Invoke the method
method.Invoke(instance, new object[] { ... });

To fit this into your code, it would look something like this:

foreach (var type in types)
{
    // Cheating here by getting the first interface, so you might want to be cleverer
    var mapInterface = type.GetInterfaces()[0];
    
    // Get the generic type of the interface, e.g. "Item"
    var genericType = mapInterface.GetGenericArguments()[0];
    
    var instance = Activator.CreateInstance(type);
    var mapType = typeof(IMap<>).MakeGenericType(genericType);
    var methodInfo = mapType.GetMethod("Mapping");

    methodInfo?.Invoke(instance, new object[] { this });
}
DavidG
  • 113,891
  • 12
  • 217
  • 223
  • I change the line of `var mapInterface = type.GetInterfaces()[0];` to `var mapInterface = type.GetInterfaces().FirstOrDefault(w => w.Name.Remove(w.Name.IndexOf('\`')) == "IMap");` – is_oz Mar 10 '21 at 14:02