bsc*_*eck 5 c# generics factory-pattern
我对编程还很陌生,并且一直负责创建一个 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",
...
}
}
Run Code Online (Sandbox Code Playgroud)
内部对象可以是任何对象,所以我认为这将是使用泛型的好机会,并按如下方式构建我的类:
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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后创建了以下界面:
public interface IWebHookModelFactory<T> where T : class, new()
{
WebHookModel<T> GetWebHookModel(string type, string jsonPayload);
}
Run Code Online (Sandbox Code Playgroud)
我不明白的是我应该如何在不知道编译时类型的情况下实现 Factory 类?
稍微玩弄模型,我将它更改为带有抽象 T 对象的抽象类,以便它可以由派生类定义。
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; }
}
Run Code Online (Sandbox Code Playgroud)
但是我仍然遇到了同样的问题,试图实现一个我在运行时不知道类型的接口。从我在网上找到的内容来看,这是一个协方差的例子,但我还没有找到任何解释如何解决这个问题的文章。是否最好跳过泛型并创建大量 case 语句?
public interface IWebHookFactory<TModel, TJsonObject>
where TJsonObject : class, new()
where TModel : WebHookModel<TJsonObject>
{
TModel GetWebHookModel(string type, string jsonPayload);
}
Run Code Online (Sandbox Code Playgroud)
我有点偏爱使用抽象类方法,因为它让我可以根据我传递到我的服务的模型来定义各个处理程序。
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();
}
}
Run Code Online (Sandbox Code Playgroud)
解决方案的要点: 1. 某个地方需要有一些虚拟调用。2. 您需要以某种方式将 JSON 负载中的类型标记映射到实际的 C# 类。
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
}
}
Run Code Online (Sandbox Code Playgroud)
然后你可以像这样反序列化:
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;
}
Run Code Online (Sandbox Code Playgroud)