在不知道类型的情况下返回通用对象?

Returning a generic object without knowing the type?

我对编程还很陌生,我的任务是创建一个 WebHook 消费者,它接收原始 JSON 字符串,将 JSON 解析为一个对象,该对象将被传递到用于处理的处理程序。 JSON 是这样进来的:

{
   "id":"1",
   "created_at":"2017-09-19T20:41:23.093Z",
   "type":"person.created",
   "object":{
      "id":"person1",
      "created_at":"2017-09-19T20:41:23.076Z",
      "updated_at":"2017-09-19T20:41:23.076Z",
      "firstname":"First",
      ...
   }
}

内部对象可以是任何对象,所以我认为这将是一个使用泛型的好机会,并按如下方式构建了我的 class:

public class WebHookModel<T> where T : class, new()
{
    [JsonProperty(PropertyName = "id")]
    public string Id { get; set; }

    [JsonProperty(PropertyName = "created_at")]
    public DateTime CreatedAt { get; set; }

    [JsonProperty(PropertyName = "type")]
    public string Type { get; set; }

    [JsonProperty(PropertyName = "object")]
    public T Object { get; set; }

    [JsonIgnore]
    public string WebHookAction
    {
        get
        {
            return string.IsNullOrEmpty(Type) ? string.Empty : Type.Split('.').Last();
        }
    }
}

然后创建了如下界面:

public interface IWebHookModelFactory<T> where T : class, new()
{
   WebHookModel<T> GetWebHookModel(string type, string jsonPayload);
}

我不明白的是我应该如何在编译时不知道类型是什么的情况下实现工厂 class?

稍微研究一下模型,我将其更改为带有抽象 T 对象的抽象 class,以便它可以由派生的 class.

定义
public abstract class WebHookModel<T> where T : class, new()
{
    [JsonProperty(PropertyName = "id")]
    public string Id { get; set; }

    [JsonProperty(PropertyName = "created_at")]
    public DateTime CreatedAt { get; set; }

    [JsonProperty(PropertyName = "type")]
    public string Type { get; set; }

    [JsonProperty(PropertyName = "object")]
    public abstract T Object { get; set; }

    [JsonIgnore]
    public string WebHookAction
    {
        get
        {
            return string.IsNullOrEmpty(Type) ? string.Empty : Type.Split('.').Last();
        }
    }
}

public PersonWebHookModel : WebHookModel<Person>
{
    public override Person Object { get; set; }
}

但我仍然 运行 陷入同样的​​问题,即尝试实现一个我在 运行 时不知道类型的接口。从我在网上找到的内容来看,这是一个协方差的例子,但我还没有找到任何解释如何解决这个问题的文章。最好是跳过泛型并创建一个庞大的 案例陈述?

public interface IWebHookFactory<TModel, TJsonObject> 
    where TJsonObject : class, new()
    where TModel : WebHookModel<TJsonObject>
{
    TModel GetWebHookModel(string type, string jsonPayload);
}

我有点偏向于使用抽象 class 方法,因为它让我可以根据传递到我的服务的模型来定义单独的处理程序。

public interface IWebHookService<TModel, TJsonObject>
    where TJsonObject : class, new()
    where TModel : WebHookModel<TJsonObject>
{
    void CompleteAction(TModel webHookModel);
}

public abstract class BaseWebhookService<TModel, TJsonObject> : IWebHookService<TModel, TJsonObject>
        where TJsonObject : class, new()
        where TModel : WebHookModel<TJsonObject>
{
    public void CompleteAction(TModel webHookModel)
    {
        var self = this.GetType();
        var bitWise = System.Reflection.BindingFlags.IgnoreCase
                        | System.Reflection.BindingFlags.Instance
                        | System.Reflection.BindingFlags.NonPublic;

        var methodToCall = self.GetMethod(jsonObject.WebHookAction, bitWise);
        methodToCall.Invoke(this, new[] { jsonObject });
    }

    protected abstract void Created(TModel webHookObject);

    protected abstract void Updated(TModel webHookObject);

    protected abstract void Destroyed(TModel webHookObject);
}

public class PersonWebHookService : BaseWebHookService<PersonWebHookModel, Person>
{
    protected override void Created(PersonWebHookModel webHookModel)
    {
        throw new NotImplementedException();
    }

    protected override void Updated(PersonWebHookModel webHookModel)
    {
        throw new NotImplementedException();
    }

    protected override void Destroyed(PersonWebHookModel webHookModel)
    {
        throw new NotImplementedException();
    }
}

解决的要点: 1. 某处需要一些虚拟呼叫。 2. 您需要以某种方式将 JSON 有效负载中的类型标记映射到实际的 C# class.
IE, "person.created"," --> 'Person'.
如果你控制序列化格式,JSON.Net 可以注入它自己的类型标签并为你做这件事。假设你不能走那条路...... 所以你需要像字典这样的东西来包含映射。

假设您的定义如下:

abstract class WebhookPayload // Note this base class is not generic! 
{
    // Common base properties here 

    public abstract void DoWork();
}

abstract class PersonPayload : WebhookPayload
{
    public override void DoWork()
    {
        // your derived impl here 
    }
}

然后你可以像这样反序列化:

    static Dictionary<string, Type> _map = new Dictionary<string, Type>
    {
        { "person.created", typeof(PersonPayload)}
    }; // Add more entries here 

    public static WebhookPayload Deserialize(string json)
    {
        // 1. only parse once!
        var jobj = JObject.Parse(json); 

        // 2. get the c# type 
        var strType = jobj["type"].ToString(); 

        Type type;
        if (!_map.TryGetValue(strType, out type))
        {
            // Error! Unrecognized type
        }

        // 3. Now deserialize 
        var obj = (WebhookPayload) jobj.ToObject(type);
        return obj;
    }