简体   繁体   English

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

[英]When to use generics and type checking?

Assume A through Z to be 26 classes I defined.假设AZ是我定义的 26 个类。 In the following example:在以下示例中:

  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;
  }

It seems to me that there is no way to shorten this other than the following:在我看来,除了以下内容之外,没有其他方法可以缩短它:

  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;
  }

Overloading the function name doesn't reduce the number of lines of code:重载函数名称不会减少代码行数:

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

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

I prefer to use the is operator to determine which Label to set, because it preserves space (in this scenario, 50 lines of meaningless brackets and 25 lines of slightly-different function names).我更喜欢使用is运算符来确定要设置哪个Label ,因为它会保留空间(在这种情况下,50 行无意义的括号和 25 行略有不同的函数名称)。 However , a Stack Overflow user recommended that I not use generics, and instead use separate functions, one for each Label .但是,Stack Overflow 用户建议我不要使用泛型,而是使用单独的函数,每个Label Although this solution will work, I prefer to not do so.尽管此解决方案可行,但我不想这样做。

Is there any benefit towards NOT using the is operator, and towards explicitly typing my functions?使用is运算符和明确键入我的函数有什么好处吗?

The benefit is that your type checking is static, rather than dynamic.好处是您的类型检查是静态的,而不是动态的。 If someone passes in a List<SomeRandomeClassYouDontSupport> to the first method, then the code will compile and just not work properly at runtime.如果有人将List<SomeRandomeClassYouDontSupport>传递给第一个方法,那么代码将被编译并且在运行时无法正常工作。 It'll either do nothing, throw an exception, or whatever you code it to do, but the point is that the caller won't be able to see that they did something wrong until they run the code .它要么什么都不做,要么抛出异常,要么你编码它做什么,但关键是调用者在运行代码之前将无法看到他们做错了什么。

When you have multiple overloads then the validation is done at compile time .当您有多个重载时,验证是在编译时完成的。 If an unsupported type is provided then the code won't even compile rather than compiling and not working.如果提供了不受支持的类型,那么代码甚至不会编译,而不是编译并且不起作用。

It's also an important semantic difference.这也是一个重要的语义差异。 Generics are there to say, "This method will work regardless of what the type is ".泛型在那里说,“无论类型是什么,这种方法都可以工作”。 When creating a list there are no right and wrong type arguments to provide.创建列表时,没有提供正确和错误的类型参数。 You can create a list of any type that you want .您可以创建您想要任何类型的列表。 That's an appropriate use of generics, because lists are a conceptually generic data structure.这是泛型的适当使用,因为列表是概念上的通用数据结构。 Having several overloads is a way of saying, "This finite list of types is supported."有多个重载是一种说法,“支持这个有限的类型列表”。 You're in the latter case, so that makes that behavior clearer to the caller, so they'll understand what the method needs to do just by looking at its signature .您处于后一种情况,因此调用者可以更清楚地了解该行为,因此他们只需查看其签名即可了解该方法需要做什么。

Having said all of that, it looks like this isn't even a situation where you should be doing either.说了这么多,看起来这甚至不是你应该做的情况。 If you really wanted to have a method accepting one of a finite number of types known at compile time as a parameter, overloads are the right way to do it, but in your case, you shouldn't be doing any of this at all.如果您真的想让方法接受在编译时已知的有限数量类型之一作为参数,那么重载是正确的方法,但在您的情况下,您根本不应该这样做。 You should be binding these UI compontents to a view as mentioned in this comment .您应该将这些 UI 组件绑定到此评论中提到的视图。

I will NOT comment about whether it is a good practice or not to do what you are doing :).我不会评论做你正在做的事情是否是一个好习惯:)。

If the absence of a label for a given list is NOT the end of the world for you and if you rely on some naming conventions for your label fields so that all labels are named for example "LabelX" where X is your type that will be used for generic lists, you can do that:如果给定列表没有标签对您来说不是世界末日,并且您依赖标签字段的一些命名约定,以便所有标签都命名为例如“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; }
}
}

Why not just make your own classes which derive their own fields automatically?为什么不让自己的类自动派生自己的字段?

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

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM