【问题标题】:Generic List as Class Property作为类属性的通用列表
【发布时间】:2015-02-08 00:28:02
【问题描述】:

我正在尝试在类的 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的集合会有不同的操作,因此需要不同的集合结果。

【问题讨论】:

  • 或者您可以将 T 替换为普通对象类型。
  • 为什么不给你的每一个操作都赋予它自己的类型,并且每一个都实现相同的接口?

标签: c# .net list generics


【解决方案1】:

由于您处理的消息很可能以字符串形式出现,无论如何您都需要对其进行解析,因此我倾向于将它们保留为如下字符串:

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

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

【讨论】:

    【解决方案2】:

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

    我实现的目的是将请求对象的集合传递给服务管理器对象;每个请求对象指定一个操作和该操作所需的任何其他参数。

    然后,我的服务实现将为收到的每个请求对象获取响应 - 响应数据的类型会有所不同 - 决定因素是请求中指定的操作。也就是说,如果我有一个“GetCatalog”操作,则该请求的结果将是List&lt;Items&gt;。相反,“GetAddressbooks”将产生List&lt;AddressbookRecords&gt;

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

    最后我结合了@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 语句来涵盖所有场景,而不是使用反射来获得灵活性。现实情况是我只是不需要在这个特定情况下的灵活性。

    我要提到的第二点(仅针对阅读此内容的任何人)的启发是“为什么”泛型不能用作类属性。事实证明,这也引起了争论。有一些争论从“它没有意义”到“微软觉得在发行版中做起来太难并放弃了它”。这些讨论可以在herehere 找到。

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

    感谢所有在寻找我的解决方案时提出建议的人。

    【讨论】:

      猜你喜欢
      • 2016-12-02
      • 2016-08-27
      • 2011-01-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-06-17
      • 2013-03-16
      • 1970-01-01
      相关资源
      最近更新 更多