簡體   English   中英

何時使用泛型和類型檢查?

[英]When to use generics and type checking?

假設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運算符和明確鍵入我的函數有什么好處嗎?

好處是您的類型檢查是靜態的,而不是動態的。 如果有人將List<SomeRandomeClassYouDontSupport>傳遞給第一個方法,那么代碼將被編譯並且在運行時無法正常工作。 它要么什么都不做,要么拋出異常,要么你編碼它做什么,但關鍵是調用者在運行代碼之前將無法看到他們做錯了什么。

當您有多個重載時,驗證是在編譯時完成的。 如果提供了不受支持的類型,那么代碼甚至不會編譯,而不是編譯並且不起作用。

這也是一個重要的語義差異。 泛型在那里說,“無論類型是什么,這種方法都可以工作”。 創建列表時,沒有提供正確和錯誤的類型參數。 您可以創建您想要任何類型的列表。 這是泛型的適當使用,因為列表是概念上的通用數據結構。 有多個重載是一種說法,“支持這個有限的類型列表”。 您處於后一種情況,因此調用者可以更清楚地了解該行為,因此他們只需查看其簽名即可了解該方法需要做什么。

說了這么多,看起來這甚至不是你應該做的情況。 如果您真的想讓方法接受在編譯時已知的有限數量類型之一作為參數,那么重載是正確的方法,但在您的情況下,您根本不應該這樣做。 您應該將這些 UI 組件綁定到此評論中提到的視圖。

我不會評論做你正在做的事情是否是一個好習慣:)。

如果給定列表沒有標簽對您來說不是世界末日,並且您依賴標簽字段的一些命名約定,以便所有標簽都命名為例如“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; }
}
}

為什么不讓自己的類自動派生自己的字段?

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