通用列表为 Class 属性
Generic List as Class Property
我正在尝试将列表 属性 上的泛型用于 class。
基本上,我使用的是基于消息的服务,该服务会接收消息请求的集合。对于收到的每个消息请求,我都会 return 一个相应的消息响应。
所以我的实现看起来像这样:
public class MessageRequest
{
private string _messageId;
private string _serviceMethod;
public MessageRequest(string id, string operation)
{
_messageId = MessageId;
_serviceMethod = operation;
}
public string MessageId { get { return _messageId; } }
public string ServiceMethod { get { return _serviceMethod; } }
}
public class MessageResponse
{
private List<T> _data; <--This does't Work..
private string _messageId;
public string MessageId { get { return _messageId; }}
public List<T> Data { get { return _data; }}
}
public List<MessageResponse> GetData(List<MessageRequest> requests)
{
List<MesssageResponse> responses = new List<MessageResponse>();
foreach(MessageRequest r in requests)
{
//I will determine the collection type for the response at runtime based
//on the MessageRequest "ServiceMethod"
List<TypeIFiguredOutFromServiceMethod> data = getData();
responses.add(new MessageResponse()
{
MessageId = r.MessageId,
Data<TypeIFiguredOutFromServiceMethod> = data
});
类似的东西...
我无法在 MessageResponse class 上指定列表类型,即:
public class MessageResponse<T>
{
}
因为MessageRequests的集合会有不同的操作,因此需要不同的集合结果。
由于您处理的消息很可能以字符串形式出现,无论如何您都需要对其进行解析,因此我倾向于将它们保留为这样的字符串:
public class MessageResponse
{
public string MessageId { get; private set; }
public Type MessageType { get; private set; }
public List<string> Data { get; private set; }
}
如果您的代码已经执行了解析,则将 string
更改为 object
并继续。
事实证明,这个话题已经在 SO 上被讨论过几次了。我将 post 我所做的,希望有人能从中受益(或者甚至有人给我一个更好的方法来完成这个)。
我实施的目的是将一组请求对象传递给服务管理器对象;每个请求对象指定一个操作和该操作所需的任何其他参数。
然后我的服务实现将为接收到的每个请求对象获取响应——响应数据的类型会有所不同——决定因素是请求中指定的操作。也就是说,如果我有一个 "GetCatalog" 的操作,该请求的结果将是 List<Items>
。相反,"GetAddressbooks" 会产生 List<AddressbookRecords>
。
这是我需要 class 上的通用 属性 的地方。我的消息响应对象将有一个通用列表作为 属性。
最后我结合使用了@Mihai Caracostea 建议使用对象和解决方案 posted here。
首先,为了清晰和高效,我修改了 MessageRequest 和 MessageResponse 对象:
public class MessageRequest
{
private readonly string _messageId;
private readonly Operation _operation;
public MessageRequest(string id, Operation operation)
{
_messageId = id;
_operation = operation;
}
public string MessageId { get { return _messageId; } }
public Operation Operation { get { return _operation; } }
}
public class MessageResponse
{
private object _data;
public MessageRequest Request { get; set; }
public T Data<T>()
{
return (T)Convert.ChangeType(_data, typeof(T));
}
public void SetData(object data)
{
_data = data;
}
}
MessageResponse 定义真正实现了这一点。使用 getter / setter 方法实现 属性 - 我使用 Object _data 字段设置从支持服务接收的数据和 T Data 基本上将数据转换为它应该的数据当客户端接收到 MessageResponse 对象读取数据时。
因此服务管理器实现如下所示:
public List<MessageResponse> GetData(List<MessageRequest> messageRequests)
{
List<MessageResponse> responses = new List<MessageResponse>();
try
{
foreach (MessageRequest request in messageRequests)
{
//Set up the proxy for the right endpoint
SetEndpoint(request);
//instantiate a new Integration Request with the right proxy and program settings
_ir = new IntegrationRequest(_proxy, ConfigureSettings(request));
MessageResponse mr = new MessageResponse { Request = request };
using (IntegrationManager im = new IntegrationManager(_ir))
{
mr.SetData(GetData(im, request));
}
responses.Add(mr);
}
return responses;
}//
catch (Exception)
{
throw;
}
使用 GetData 方法结果的客户端实现如下所示:
List<MessageRequest> requests = new List<MessageRequest>();
requests.Add(new MessageRequest(Guid.NewGuid().ToString(), Operation.GetBudgets));
requests.Add(new MessageRequest(Guid.NewGuid().ToString(), Operation.GetCatalogItems));
List<MessageResponse> responses;
using (ServiceManager sm = new ServiceManager())
{
responses = sm.GetData(requests);
}
if (responses != null)
{
foreach (var response in responses)
{
switch (response.Request.Operation)
{
case Operation.GetBudgets:
List<Budget> budgets = response.Data<List<Budget>>();
break;
case Operation.GetCatalogItems:
List<Item> items = response.Data<List<Item>>();
break;
}
}
}
这只是一个测试 - 但基本上我构建了两个 MessageRequest 对象(获取预算和获取目录项)- posted 到服务并返回 MessageResponse 对象的集合。
这适用于我需要它做的事情。
关于这个主题,我还想提另外两点,我看了一下是使用反射来确定运行时的响应类型。我能够做到这一点的方法是在操作枚举上指定自定义属性类型:
public enum Operation
{
[DA.Services.ResponseType (Type = ResponseType.CreateOrder)]
CreateOrder,
[DA.Services.ResponseType(Type = ResponseType.GetAddressbooks)]
GetAddressbooks,
[DA.Services.ResponseType(Type = ResponseType.GetCatalogItems)]
GetCatalogItems,
[DA.Services.ResponseType(Type = ResponseType.GetAddressbookAssociations)]
GetAddressbookAssociations,
[DA.Services.ResponseType(Type = ResponseType.GetBudgets)]
GetBudgets,
[DA.Services.ResponseType(Type = ResponseType.GetUDCTable)]
GetUDCTable
}
class ResponseType : System.Attribute
{
public string Type { get; set; }
public const string CreateOrder = "Models.Order";
public const string GetAddressbooks = "Models.AddressbookRecord";
public const string GetCatalogItems = "Models.Item";
public const string GetAddressbookAssociations = "Models.AddressbookAssociation";
public const string GetBudgets = "Models.Budget";
public const string GetUDCTable = "Models.UdcTable";
}
我主要研究了使用 Activator.CreateType() 通过对请求中指定的操作评估 ResponseType.Type 来为客户端动态创建响应对象。
虽然这很优雅 - 我发现不值得花费时间来处理。此实现具有多年未更改的相当明确的对象。我愿意写一个 switch 语句来覆盖所有场景,而不是为了灵活性而使用反射。事实上,我只是不需要在这个特定情况下的灵活性。
我想提的第二点(仅供阅读本文的任何人)启发是 "Why" 泛型不能用作 class 属性。事实证明,这也引起了争论。争论的范围从 "it doesn't make sense" 到 "microsoft felt it was too hard to do in the release and abandoned it"。可以在 here 和 此处 找到这些讨论。
最后,其中一个线程出于技术原因提供了 link。原因是编译器无法确定为具有通用 属性 的对象分配多少内存。这篇文章的作者是 Julian Bucknail,可以找到 here。
感谢所有 post 提出建议以找到我的解决方案的人。
我正在尝试将列表 属性 上的泛型用于 class。
基本上,我使用的是基于消息的服务,该服务会接收消息请求的集合。对于收到的每个消息请求,我都会 return 一个相应的消息响应。
所以我的实现看起来像这样:
public class MessageRequest
{
private string _messageId;
private string _serviceMethod;
public MessageRequest(string id, string operation)
{
_messageId = MessageId;
_serviceMethod = operation;
}
public string MessageId { get { return _messageId; } }
public string ServiceMethod { get { return _serviceMethod; } }
}
public class MessageResponse
{
private List<T> _data; <--This does't Work..
private string _messageId;
public string MessageId { get { return _messageId; }}
public List<T> Data { get { return _data; }}
}
public List<MessageResponse> GetData(List<MessageRequest> requests)
{
List<MesssageResponse> responses = new List<MessageResponse>();
foreach(MessageRequest r in requests)
{
//I will determine the collection type for the response at runtime based
//on the MessageRequest "ServiceMethod"
List<TypeIFiguredOutFromServiceMethod> data = getData();
responses.add(new MessageResponse()
{
MessageId = r.MessageId,
Data<TypeIFiguredOutFromServiceMethod> = data
});
类似的东西...
我无法在 MessageResponse class 上指定列表类型,即:
public class MessageResponse<T>
{
}
因为MessageRequests的集合会有不同的操作,因此需要不同的集合结果。
由于您处理的消息很可能以字符串形式出现,无论如何您都需要对其进行解析,因此我倾向于将它们保留为这样的字符串:
public class MessageResponse
{
public string MessageId { get; private set; }
public Type MessageType { get; private set; }
public List<string> Data { get; private set; }
}
如果您的代码已经执行了解析,则将 string
更改为 object
并继续。
事实证明,这个话题已经在 SO 上被讨论过几次了。我将 post 我所做的,希望有人能从中受益(或者甚至有人给我一个更好的方法来完成这个)。
我实施的目的是将一组请求对象传递给服务管理器对象;每个请求对象指定一个操作和该操作所需的任何其他参数。
然后我的服务实现将为接收到的每个请求对象获取响应——响应数据的类型会有所不同——决定因素是请求中指定的操作。也就是说,如果我有一个 "GetCatalog" 的操作,该请求的结果将是 List<Items>
。相反,"GetAddressbooks" 会产生 List<AddressbookRecords>
。
这是我需要 class 上的通用 属性 的地方。我的消息响应对象将有一个通用列表作为 属性。
最后我结合使用了@Mihai Caracostea 建议使用对象和解决方案 posted here。
首先,为了清晰和高效,我修改了 MessageRequest 和 MessageResponse 对象:
public class MessageRequest
{
private readonly string _messageId;
private readonly Operation _operation;
public MessageRequest(string id, Operation operation)
{
_messageId = id;
_operation = operation;
}
public string MessageId { get { return _messageId; } }
public Operation Operation { get { return _operation; } }
}
public class MessageResponse
{
private object _data;
public MessageRequest Request { get; set; }
public T Data<T>()
{
return (T)Convert.ChangeType(_data, typeof(T));
}
public void SetData(object data)
{
_data = data;
}
}
MessageResponse 定义真正实现了这一点。使用 getter / setter 方法实现 属性 - 我使用 Object _data 字段设置从支持服务接收的数据和 T Data 基本上将数据转换为它应该的数据当客户端接收到 MessageResponse 对象读取数据时。
因此服务管理器实现如下所示:
public List<MessageResponse> GetData(List<MessageRequest> messageRequests)
{
List<MessageResponse> responses = new List<MessageResponse>();
try
{
foreach (MessageRequest request in messageRequests)
{
//Set up the proxy for the right endpoint
SetEndpoint(request);
//instantiate a new Integration Request with the right proxy and program settings
_ir = new IntegrationRequest(_proxy, ConfigureSettings(request));
MessageResponse mr = new MessageResponse { Request = request };
using (IntegrationManager im = new IntegrationManager(_ir))
{
mr.SetData(GetData(im, request));
}
responses.Add(mr);
}
return responses;
}//
catch (Exception)
{
throw;
}
使用 GetData 方法结果的客户端实现如下所示:
List<MessageRequest> requests = new List<MessageRequest>();
requests.Add(new MessageRequest(Guid.NewGuid().ToString(), Operation.GetBudgets));
requests.Add(new MessageRequest(Guid.NewGuid().ToString(), Operation.GetCatalogItems));
List<MessageResponse> responses;
using (ServiceManager sm = new ServiceManager())
{
responses = sm.GetData(requests);
}
if (responses != null)
{
foreach (var response in responses)
{
switch (response.Request.Operation)
{
case Operation.GetBudgets:
List<Budget> budgets = response.Data<List<Budget>>();
break;
case Operation.GetCatalogItems:
List<Item> items = response.Data<List<Item>>();
break;
}
}
}
这只是一个测试 - 但基本上我构建了两个 MessageRequest 对象(获取预算和获取目录项)- posted 到服务并返回 MessageResponse 对象的集合。
这适用于我需要它做的事情。
关于这个主题,我还想提另外两点,我看了一下是使用反射来确定运行时的响应类型。我能够做到这一点的方法是在操作枚举上指定自定义属性类型:
public enum Operation
{
[DA.Services.ResponseType (Type = ResponseType.CreateOrder)]
CreateOrder,
[DA.Services.ResponseType(Type = ResponseType.GetAddressbooks)]
GetAddressbooks,
[DA.Services.ResponseType(Type = ResponseType.GetCatalogItems)]
GetCatalogItems,
[DA.Services.ResponseType(Type = ResponseType.GetAddressbookAssociations)]
GetAddressbookAssociations,
[DA.Services.ResponseType(Type = ResponseType.GetBudgets)]
GetBudgets,
[DA.Services.ResponseType(Type = ResponseType.GetUDCTable)]
GetUDCTable
}
class ResponseType : System.Attribute
{
public string Type { get; set; }
public const string CreateOrder = "Models.Order";
public const string GetAddressbooks = "Models.AddressbookRecord";
public const string GetCatalogItems = "Models.Item";
public const string GetAddressbookAssociations = "Models.AddressbookAssociation";
public const string GetBudgets = "Models.Budget";
public const string GetUDCTable = "Models.UdcTable";
}
我主要研究了使用 Activator.CreateType() 通过对请求中指定的操作评估 ResponseType.Type 来为客户端动态创建响应对象。
虽然这很优雅 - 我发现不值得花费时间来处理。此实现具有多年未更改的相当明确的对象。我愿意写一个 switch 语句来覆盖所有场景,而不是为了灵活性而使用反射。事实上,我只是不需要在这个特定情况下的灵活性。
我想提的第二点(仅供阅读本文的任何人)启发是 "Why" 泛型不能用作 class 属性。事实证明,这也引起了争论。争论的范围从 "it doesn't make sense" 到 "microsoft felt it was too hard to do in the release and abandoned it"。可以在 here 和 此处 找到这些讨论。
最后,其中一个线程出于技术原因提供了 link。原因是编译器无法确定为具有通用 属性 的对象分配多少内存。这篇文章的作者是 Julian Bucknail,可以找到 here。
感谢所有 post 提出建议以找到我的解决方案的人。