通用列表为 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 提出建议以找到我的解决方案的人。