何时使用泛型和类型检查?

时间:2015-07-28 20:09:28

标签: c# generics reflection

假设AZ为我定义的26个类。在以下示例中:

  private List<A> _listA;
  private List<B> _listB;
  // private List<C>, and so on, through...
  private List<Z> _listZ;

  private void setLabelA()
  {
      LabelA.Text = _listA.Count;
  }

  // private void setLabelB() exists
  // and so does setLabelC()
  // and so on, all the way through to...

  private void setLabelZ()
  {
      LabelA.Text = _listZ.Count;
  }

在我看来,除了以下内容之外,没有其他方法可以缩短它:

  private void setLabel<genericType>(List<genericType> list)
  {
      if(list is List<A>)      LabelA.Text = _listA.Count;
      else if(list is List<B>) LabelB.Text = _listB.Count;
      else if(list is List<C>) LabelC.Text = _listC.Count;
      //  and so on...
      else if(list is List<Z>) LabelZ.Text = _listZ.Count;
  }

重载函数名称不会减少代码行数:

  private void setLabel(List<A> list)
  {
      LabelA.Text = _listA.Count;
  }

  private void setLabel(List<B> list)
  {
      LabelB.Text = _listB.Count;
  }

我更喜欢使用is运算符来确定要设置的Label,因为它会保留空间(在这种情况下,50行无意义括号和25行略有不同的函数名称)。 但是,Stack Overflow用户建议我使用泛型,而是使用单独的函数,每个Label一个。虽然这个解决方案可行,但我不愿意这样做。

使用is运算符对 NOT 有什么好处,并明确键入我的函数?

3 个答案:

答案 0 :(得分:5)

好处是您的类型检查是静态的,而不是动态的。如果有人将List<SomeRandomeClassYouDontSupport>传递给第一个方法,那么代码将编译并且在运行时无法正常工作。它不会做任何事情,抛出异常,或者你要编写的任何代码,但重点是调用者无法看到他们做错了直到他们运行代码

如果有多个重载,则验证在编译时完成。如果提供了不受支持的类型,那么代码甚至不会编译而不是编译而不能正常工作。

它也是一个重要的语义差异。泛型可以说,&#34;无论类型是什么&#34;此方法都将起作用。创建列表时,没有提供正确和错误的类型参数。您可以创建任何类型的列表。这是对泛型的适当使用,因为列表是概念上通用的数据结构。有几个重载是一种说法,&#34;支持这种有限的类型列表。&#34;在后一种情况下,这使得调用者的行为更加清晰,因此他们只需查看其签名即可了解该方法需要做什么。

说完所有这些之后,看起来这甚至都不是你应该做的事情。如果你真的想让一个方法接受在编译时已知的有限数量类型之一作为参数,那么重载是正确的方法,但在你的情况下,你不应该做任何这样的事情。所有。您应该将这些UI组件绑定到this comment中提到的视图。

答案 1 :(得分:0)

我不会评论做你正在做的事情是不是一个好习惯。)。

如果给定列表的标签不存在并不是您的世界末日,并且您依赖于标签字段的某些命名约定,那么所有标签都以例如“LabelX”命名,其中X是您的类型将用于通用列表,你可以这样做:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Labels
{
class Program
{
    static void Main(string[] args)
    {
        Container c = new Container();
        c.ApplyLabels();
    }
}

public class A
{
}

public class B
{
}

public class C
{
}
public class Container
{
    private Label LabelA = new Label ();
    private Label LabelB = new Label ();
    private Label LabelC = new Label ();

    private List<A> _listA = new List<A> ();
    private List<B> _listB = new List<B> ();
    private List<C> _listC = new List<C> ();

    public void ApplyLabels ()
    {
        var allFields = this.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

        Dictionary<Type, FieldInfo> listFields = new Dictionary<Type, FieldInfo>();
        Dictionary<Type, FieldInfo> labelMappings = new Dictionary<Type, FieldInfo>();
        Dictionary<string, Type> namespacesForListGenericTypes = new Dictionary<string, Type>();

        List<FieldInfo> possibleLabelFields = new List<FieldInfo>();
        foreach (var field in allFields)
        {
            if (field.FieldType.IsGenericType)
            {
                var genericTypeDef = field.FieldType.GetGenericTypeDefinition();

                if (genericTypeDef == typeof (List<>))
                {
                    var genericArgument = field.FieldType.GetGenericArguments()[0];
                    listFields.Add(genericArgument, field); // remember list fields and for each list what generic type it has!

                    namespacesForListGenericTypes[genericArgument.Name] = genericArgument;
                }
            }
            else if (typeof (Label).IsAssignableFrom (field.FieldType))
            {
                possibleLabelFields.Add(field);
            }
        }

        foreach (var possible in possibleLabelFields)
        {
            if (possible.Name.Length < 6) continue;

            var typeName = possible.Name.Substring(5);

            Type genericListType;
            if (namespacesForListGenericTypes.TryGetValue (typeName, out genericListType))
            {
                labelMappings[genericListType] = possible;
            }
        }

        foreach (var list in listFields)
        {
            FieldInfo destination;

            if (false == labelMappings.TryGetValue (list.Key, out destination))
            {
                continue;
            }

            var destinationLabel = destination.GetValue(this) as Label;
            if (destinationLabel == null) continue;

            var listValue = list.Value.GetValue(this) as IList;
            var cnt = listValue == null ? 0 : listValue.Count;

            destinationLabel.Text = cnt.ToString();
        }
    }
}

public class Label
{
    public string Text { get; set; }
}
}

答案 2 :(得分:0)

为什么不自己创建自己派生自己字段的类呢?

private class ListWithText : List<T>
{
    int Text {
        get { return this.Count; }
    }
}

ListWithText<A> LabelA = new ListWithText<A>();
Console.WriteLine(LabelA.Text);