转换泛型参数

byg*_*ace 2 c# generics

我从一个方法中得到一个对象.该对象属于object类型,由于向后兼容性,我无法更改此对象.如果它是某种类型(Response<T>波纹管),那么我需要访问属性Payload类型的属性,T以便我可以将其序列化为另一个对象的一部分并将其发送出去.问题是因为我不知道T我不能将对象转换Response<T>为访问类型,Payload即使我不关心它的类型.

这是我的通用类型的对象:

public class Response
{
   public int Status { get; set; }
   public string Message { get; set; }
}

public class Response<T> : Response
{
   public T Payload { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

这是我想做的事情:

// Will sometimes be of type Response<T>
object data = LegacyCode();
if (data.GetType().IsGenericType && data.GetType().GetGenericTypeDefinition() == typeof(Response<>)) {
   var payload = ((Response<object>)data).Payload; // Unable to cast object of type...
}
Run Code Online (Sandbox Code Playgroud)

但我能找到的唯一方法是使用动力学.

// Will sometimes be of type Response<T>
object data = LegacyCode();
if (data.GetType().IsGenericType && data.GetType().GetGenericTypeDefinition() == typeof(Response<>)) {
   var payload = ((dynamice)data).Payload;     
}
Run Code Online (Sandbox Code Playgroud)

不要问为什么事情就像他们一样(我想知道自己).我必须做代码体操以保持在这个系统中的向后兼容性.我只想要编译时检查属性的名称.

这是一个小提琴:https://dotnetfiddle.net/dXxHbD

更新:

我需要能够序列化和反序列化这个对象.最初Response有一个Payload类型的财产object.这在Response<T>反序列化时导致序列化问题,因为该Payload属性的类型Newtonsoft.Json.Linq.JObject无法转换为T.这是一个例子:https://dotnetfiddle.net/uc15HD

问题是,我是走错了方向和反序列化工作,如果我投Tobject,而不是试图投objectT.当我将值存储为其特定类型时T,序列化程序知道要将字符串反序列化为什么.以下是Jon的答案:https : //dotnetfiddle.net/KwudAx

以下是使用Matias使用协方差的解决方案的类似示例:https://dotnetfiddle.net/kCjZr4

Jon*_*eet 5

要获得对属性名称的编译时检查,您可以保持动态类型,但获取运行时"迷你编译器"来完成这项工作:

object data = LegacyCode();
object payload = GetPayload(data);
// Use payload

...

private static object GetPayload<T>(Response<T> response)
{
    return response.Payload;
}

public static object GetPayload(object data)
{
    // Fallback method. You could return null here and use
    // that to indicate in the calling code that it wasn't a
    // Response<T>...
}
Run Code Online (Sandbox Code Playgroud)

更好的解决方案是添加非泛型接口或额外的基类.例如:

public class Response
{
   public int Status { get; set; }
   public string Message { get; set; }
}

public interface IPayloadHolder
{
    public object Payload { get; }
}

public class Response<T> : Response, IPayloadHolder
{
   public T Payload { get; set; }

   // By using explicit interface implementation, this
   // doesn't get in the way for normal usage.
   IPayloadHolder.Payload { get { return Payload; } }
}
Run Code Online (Sandbox Code Playgroud)

然后你可以使用:

var payloadHolder = data as IPayloadHolder;
if (payloadHolder != null)
{
    var payload = payloadHolder.Payload;
}
Run Code Online (Sandbox Code Playgroud)


Mat*_*zer 5

我相信你需要使用协方差.

设计界面IResponse<out T>:

public interface IResponse<out T>
{
   public T Payload { get; }
}
Run Code Online (Sandbox Code Playgroud)

并实施它Response<T>.现在你可以把它投射到IResponse<object>:

Response<string> x = new Response<string>();
IResponse<object> y = x; // implicit cast
Run Code Online (Sandbox Code Playgroud)