【问题标题】:How to put different objects in a List如何将不同的对象放在列表中
【发布时间】:2015-05-05 04:43:23
【问题描述】:

我有一个网页,用户可以在其中将某些小部件拖放到一个占位符中。每个小部件都有自己的一组属性。

例如,假设我有以下 HTML 小部件:

Widget1:
 - Title field
 - Technical name field

还有一个更动态的小部件:

Widget2:
 - Range picker field 1 (0 to 100)
 - Range picker field 2 (0 to 100)
               ------------------
               | (+) Add picker |
               ------------------
 (can add as many range pickers as needed)

现在只需单击一个按钮,这些设置就会保存到数据库中。但在某些时候,我还需要再次从中读取数据。

例如,假设我有以下小部件类:

interface IWidget { }

class Widget1 : IWidget
{
    public string Title {get;set;}
    public string TechnicalName {get;set;}
}

class Widget2 : IWidget
{
    public List<int> PickerValues {get;set;}
}

然后将其存储在List

var widgets = new List<IWidget>();
widgets.Add(new Widget1());
widgets.Add(new Widget2());

那会奏效。但是再次从该列表中获取对象是一个问题:

var widget = widgets.First();
widget.?????   // <-- NO INTELLISENSE!

这意味着它不知道它有什么实际的实现。我必须投它。但是,如果我不知道那里存储了哪些小部件,该怎么办?它可以是 1、5 甚至 10 个不同小部件的列表,以任意顺序排列。

我该如何解决这个问题?

【问题讨论】:

  • 使用接口创建抽象的要点是您不应该关心具体的实现。目前,您的界面毫无意义。你想用这些小部件做什么?
  • 从列表中拉出 IWidget 后,您想对它做什么?
  • 你可以看到关于asis关键字
  • IWidget 没有定义任何成员,因此您不会获得任何智能感知。您需要在界面中放置常用功能,否则是的,您将不得不转换。

标签: c# .net asp.net-core-mvc


【解决方案1】:

您当前拥有一个全部实现IWidget 接口的对象列表。直接访问它们只会让您对IWidget(没有)的属性进行智能感知。

为了解决这个问题,您要么需要在IWidget 接口中放置一些属性,要么检查每个项目的类型然后进行转换,例如:

IWidget widget = widgets.First();

var widget1 = widget as Widget1;

if (widget1 != null)
{
    widget1.TechnicalName = "new name"; // <-- We have intellisense
}

您还可以有一些逻辑来循环检查每个小部件,例如:

foreach (var widget in widgets)
{
    if (widget is Widget1)
    {
        var widget1 = (Widget1) widget;
        // Do something with Widget1 types here
    }
    else if (widget is Widget2)
    {
        var widget2 = (Widget2)widget;
        // Do something with Widget2 types here
    }
}

或者,您可以通过执行以下操作从列表中获取第一个 Widget1(或 Widget2):

Widget1 widget = widgets.OfType<Widget1>().First();

【讨论】:

    【解决方案2】:

    您将需要使用多态来解决您的问题:

    interface IWidget 
    {
        public void readData();
    }
    
    class Widget1 : IWidget
    {
        public string Title { get; set; }
        public string TechnicalName { get; set; }
    
        public void readData()
        {
            Console.WriteLine("- Title: {0}\n- Technical name: {1}", Title, TechnicalName);
        }
    }
    
    class Widget2 : IWidget
    {
        public List<int> PickerValues { get; set; }
    
        public void readData()
        {
            StringBuilder builder = new StringBuilder();
            for (int i = 1; i <= PickerValues.Count; i++)
            {
                builder.AppendLine(String.Format("- Range picker field {0}: {1}",i,PickerValues[i-1]));
            }
            Console.WriteLine(builder.ToString());
        }
    }
    

    后来……

            var widgets = new List<IWidget>();
            widgets.Add(new Widget1());
            widgets.Add(new Widget2());
            var widget = widgets.First();
            widget.readData(); // will print correct output for each type of widget, as implemented on each widget.
    

    【讨论】:

      【解决方案3】:

      也许您的选择是先对实例进行类型检查:

      var widget = widgets.First();
      if (widget is Widget1)
      {
          var widget1 = (Widget1) widget;
          widget1.DoSomethingSpecificToWidget1();
      }
      

      或者,使用附加的 LinQ 功能,您可以轻松地将列表拆分为通用子列表。如果您不需要按照它们存储在widgets 变量中的顺序处理小部件,这将很有效。

      var w1List = widgets.OfType<Widget1>();// you may also call `.ToList()`
      var w2List = widgets.OfType<Widget2>();
      
      foreach (var w1 in w1List)
      {
          w1.DoSomethingSpecificToWidget1(); // w1 is now Widget1
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-10-15
        • 2022-07-06
        • 1970-01-01
        • 1970-01-01
        • 2011-06-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多