通用列表作为类属性

时间:2015-02-08 00:28:03

标签: c# .net list generics

我正在尝试在类的List属性上使用泛型。

基本上我使用的是基于消息的服务,它将接收一组消息请求。对于收到的每个消息请求,我将返回相应的消息响应。

所以我的实现看起来像这样:

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类中指定列表类型:

public class MessageResponse<T>
{
}

因为MessageRequests的集合将具有不同的操作,因此将需要不同的收集结果。

2 个答案:

答案 0 :(得分:2)

由于您处理的信息最有可能是您需要解析的字符串,我倾向于将它们保存为这样的字符串:

public class MessageResponse
{
    public string MessageId { get; private set; }
    public Type MessageType { get; private set; }
    public List<string> Data { get; private set; }
}

如果您的代码已经执行了解析,请将string更改为object并继续使用。

答案 1 :(得分:0)

因为事实证明这个话题在SO上已被讨论了几次。我将发布我所做的事情,希望有人可以从中受益(甚至有人给我一个更好的方法来实现这一点)。

我的实现的目的是将一组请求对象传递给Service Manager对象;每个请求对象指定一个操作以及该操作所需的任何其他参数。

然后,我的服务实现将为每个收到的请求对象获取响应 - 响应数据的类型会有所不同 - 行列式是请求中指定的操作。也就是说,如果我的操作是&#34; GetCatalog&#34;,那么该请求的结果将是List<Items>。相反,一个&#34; GetAddressbooks&#34;会产生List<AddressbookRecords>

这是我需要一个类的通用属性的地方。我的消息响应对象将具有通用List作为属性。

最后,我最终使用@Mihai Caracostea建议组合使用对象和发布的解决方案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对象(获取预算,获取目录项) - 发布到服务和返回的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语句来涵盖所有场景,而不是使用反射来提高灵活性。现实情况是,我并不需要在这个特定情况下具有灵活性。

我想提到的第二点(仅适用于读过这篇文章的人)启发是#34;为什么&#34;泛型不能用作类属性。事实证明,这也是有争议的。有些论据来自&#34;它没有意义&#34;微软认为在发布中做起来太难了而放弃了#34;。可以在herehere找到这些讨论。

最后,其中一个线程提供了一个技术原因的链接。这个原因是编译器无法确定为具有泛型属性的对象分配多少内存。该文章的作者是Julian Bucknail,可以找到here

感谢大家在找到我的解决方案时提出的建议。