转换为通用类型

时间:2018-01-28 11:01:29

标签: c# dynamic reflection async-await

我有以下界面

public interface IMessageData {}

public interface IMessageSender<T> where T : class, IMessageData, new()
{
    Task<T> BuildType();
    Task SendMessage(T message);
}

然后是IMessageSender

的基础实现
public abstract class MessageSenderBase<T> : IMessageSender<T> where T : class, IMessageData, new()
{
    public MessageSenderBase() {
    }

    public abstract Task<T> BuildType();

    public async Task SendMessage(T message) {
        Console.WriteLine($"Sending {JsonConvert.SerializeObject(message)}");
        await Task.FromResult(0);
    }
}

要使用这些接口,我通常会创建一个MessageData的新实例,并创建一个MessageSenderBase的特定实现,如

public class MessageDataA : IMessageData {
    public MessageDataA() {
    }

    public string Description { get; set; }
}

public class ASender : MessageSenderBase<MessageDataA> {
    public override async Task<MessageDataA> BuildType() {
        MessageDataA result = new MessageDataA() { Description = "description" };
        return await Task.FromResult<MessageDataA>(result);
    }
}

在运行时我只在配置中有MessageSenderBase实现的名称,我需要创建这些类的动态实例并调用这两个方法。使用以下代码可以正确实现:

var className = "ASender";
var buildMethodName = "BuildType";
var sendMethodName = "SendMessage";

Assembly assembly = Assembly.GetExecutingAssembly();
var type = assembly.GetType(className);
var instance = Activator.CreateInstance(type);

//invoke build method
var buildMethodInfo = type.GetMethod(buildMethodName);
dynamic buildAwaitable = buildMethodInfo.Invoke(instance, null);
var returnedType = await buildAwaitable;

//invoke send method
var sendMethodInfo = type.GetMethod(sendMethodName);
dynamic sendAwaitable = sendMethodInfo.Invoke(instance, new[] { returnedType });
await sendAwaitable;

我的问题:

  1. 我可以将var instance = Activator.CreateInstance(type);转换为更强的类型而不是将其保留为var吗?这样可以避免我使用MethodInfo和Invoke来调用方法。
  2. 作为进一步的建议是否有任何方法可以使这种设计更加强大?

2 个答案:

答案 0 :(得分:1)

  

我可以转换var instance = Activator.CreateInstance(type);更强的类型,而不是将其作为var?这样可以避免我使用MethodInfo和Invoke来调用方法。

var并不代表无类型。它是右侧表达式的类型。在这种情况下,它是object,但您可以将其转换为实际预期的类型。但是,在动态场景中,还需要做更多工作(见下文)。

  

作为进一步的建议是否有任何方法可以使这种设计更加强大?

常见的解决方案是为您描述的情况设置非通用接口。优点是您可以直接调用方法,但您必须依赖object而不是通过泛型参数T提供的某些特定类型。

接口的基本合约将如下所示:

public interface IMessageData {}

public interface IMessageSender
{
    Task<object> BuildType();
    Task SendMessage(object message);
}

public interface IMessageSender<T> : IMessageSender where T : class, IMessageData, new()
{
    Task<T> BuildType();
    Task SendMessage(T message);
}

在基本实现中,IMessageSender的显式实现将帮助您从其通用对应物中隐藏弱类型接口。实现只会将调用传递给泛型方法:

public abstract class MessageSenderBase<T> : IMessageSender<T> where T : class, IMessageData, new()
{
    public MessageSenderBase() {
    }

    Task<object> IMessageSender.BuildType() {
        T result = await BuildType();
        return result;
    }

    Task IMessageSender.SendMessage(object message) {
        return SendMessage((T)message);
    }

    public abstract Task<T> BuildType();

    public async Task SendMessage(T message) {
        …
    }
}

然后用法比原始代码简单得多:

var className = "ASender";
var buildMethodName = "BuildType";
var sendMethodName = "SendMessage";

Assembly assembly = Assembly.GetExecutingAssembly();
var type = assembly.GetType(className);
IMessageSender instance = (IMessageSender)Activator.CreateInstance(type);

//invoke build method
var result = await instance.BuildType();

//invoke send method
await instance.SendMessage(result);

答案 1 :(得分:1)

您可以通过提供非通用接口完全绕过此问题。仔细观察,您实际上并不需要公开BuildType方法。

您最终会得到一个公开单个方法的IMessageSender接口:

public interface IMessageSender
{
    Task SendMessage();
}

然后抽象类提供BuildType方法并将所有内容连接在一起:

public abstract class MessageSenderBase<T> : IMessageSender where T : class, IMessageData, new()
{
    public MessageSenderBase() {
    }

    protected abstract Task<T> BuildType();

    public async Task SendMessage() {
        var message = await BuildType();
        Console.WriteLine($"Sending {JsonConvert.SerializeObject(message)}");
        await Task.FromResult(0);
    }
}