首页 > 解决方案 > C# 方法调用无法将实现接口的类转换为所述接口

问题描述

所以我有一个应用程序将 websocket 请求发送到服务器并记录响应以供以后查看。我希望用户能够检查他们想要记录的响应。我有获取所有不同响应类型的方法,并且每种响应类型都实现了 IWebSocketResponseData。然后我有一个方法,它根据传入的 CheckBoxData 列表返回所有响应类型,它获取从服务器检查的所有响应,并且应该返回 List<IWebSocketResponseData>。

public async Task<List<IWebSocketResponseData>> GetData(List<CheckBoxData> dataToGet)
        {
            var toReturn = new List<IWebSocketResponseData>();
            foreach (var data in dataToGet)
            {
                var method = GetType()
                    .GetMethods(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance)
                    .SingleOrDefault(m => m.ReturnType.GenericTypeArguments.Contains(data.ResponseType));
                IWebSocketResponseData response = await (Task<IWebSocketResponseData>)method.Invoke(this, null);
                toReturn.Add(response);
            }
            return toReturn;
        }

        public async Task<ModuleInfoResponse> GetModuleInfo()
        {
            await SendAsync(JsonConvert.SerializeObject(new
            {
                type = "fetch",
                item = "modinfo"
            }));
            return await ReceiveAsync<ModuleInfoResponse>();
        }

        public async Task<NetworkSettingsResponse> GetNetworkSettings()
        {
            await SendAsync(JsonConvert.SerializeObject(new
            {
                type = "fetch",
                item = "network"
            }));
            return await ReceiveAsync<NetworkSettingsResponse>();
        }

GetData 下面的两个方法是将从 GetData 方法调用的方法的示例。请记住 ModuleInfoResponse 和 NetworkSettingsResponse 都实现 IWebSocketDataResponse 这基本上只是一个标签,即

public interface IWebSocketDataResponse { }

这给了我一个 InvalidCastException 试图从任何具体的响应类(即 ModuleInfoResponse、NetworkSettingsResponse ......为什么?

编辑:我意识到方法没有空检查,因为我知道我会为此得到一些热量

标签: c#.netpolymorphism

解决方案


这是因为Task不是协变的。在 C#中, variance只能应用于泛型接口(和委托),因此您可以执行以下操作:

public interface IWebSocketDataResponse { }
public class ModuleInfoResponse : IWebSocketDataResponse { }
interface IFoo<out T> // or just interface IFoo<out T> {} for testing purposes 
{ 
    T GetSomething();
    // The following statement generates a compiler error.
    // void SetSomething(T sampleArg);
}

IFoo<ModuleInfoResponse> iX = ...;
IFoo<IWebSocketDataResponse> IXX = iX;

但不能:

Task<IWebSocketDataResponse> x = Task.FromResult<ModuleInfoResponse>(null);

另请阅读此答案

UPD

一种选择是继续使用反射,如下所示:

var task = (Task)method.Invoke(this, null);
await task;
var response = (IWebSocketResponseData)task.GetType().GetProperty("Result").GetValue(task);

推荐阅读