簡體   English   中英

C# - 切換泛型

[英]C# - switch in generics

我試圖在泛型方法中的開關上比較泛型類型參數。 這對我的解決方案不起作用。 原因是:參數必須是特定類型(bool,char,string,integral,enum)。

    public T testfunction<T, U>(U numb)
    {
        switch(numb){ //<-- error

        }
        ....
    }

但它背后的意義是什么? 如果參數是通用的並且我想進行比較,為什么它必須是類型定義的變量?

你想在switch語句中測試什么? 當然你必須知道有關正在進入的物體類型的東西。

考慮一下:當您可以在方法中接受Product或Customer類型時,如何構造switch語句? 您希望編譯器為您做出的邏輯選擇是什么? 如果希望編譯器根據產品價格選擇操作,則不適用於Customer對象。 但是,如果產品和客戶都有您想要轉向的CreateDate字段,則可以將其提取到接口中,並將其用作方法的通用約束。

為您的通用方法簽名添加適當的約束,該簽名封裝了您對所期望的類型的了解,然后您將能夠切換:

public interface ICreateDate {

   public DateTime CreateDate { get; set; }

}

 public T testfunction<T, U>(U numb) where U : ICreateDate
    {
        switch(numb.CreateDate.DayOfWeek){

            case DayOfWeek.Monday:

        }
        ....
    }

這不起作用,因為switch內部的case部分需要是特定類型的編譯時常量。 例如,你不能做case 1:因為numb可能是一個字符串; 你也不能做case "foo":因為numb可能是一個整數。 必須在編譯時知道numb的類型才能將其用作切換變量,因為編譯器需要知道在case部分中哪種常量值有效。

這背后的邏輯是switch語句不適用於所有類型,因此它不會讓你打開泛型類型。

實際上它適用於很少的類型,int,char,string,bool(在這一個上不確定),也許還有一些更原始的類型。

原始類型是內置於語言中的類型,基本上所有這些都不是類/結構/聯合等...

正如許多其他人所說,switch語句必須是編譯時常量,如C#語言規范(在此處: http//www.microsoft.com/en-us/download/details.aspx?id = 7029 )中所述。 8.7.2:

switch語句由關鍵字switch,后跟括號表達式(稱為switch表達式),后跟switch-block組成。 開關組由零個或多個開關部分組成,用括號括起來。 每個switch-section包含一個或多個switch-labels,后跟一個statement-list(第8.2.1節)。 switch語句的控制類型由switch表達式建立。

•如果switch表達式的類型是sbyte,byte,short,ushort,int,uint,long,ulong,bool,char,string或enum-type,或者它是與這些類型之一對應的可空類型那么這就是switch語句的管理類型。

•否則,從switch表達式的類型到以下可能的控制類型之一,必須存在一個用戶定義的隱式轉換(第6.4節):sbyte,byte,short,ushort,int,uint,long,ulong,char, string,或者,與這些類型之一對應的可空類型。

•否則,如果不存在此類隱式轉換,或者存在多個此類隱式轉換,則會發生編譯時錯誤。

每個case標簽的常量表達式必須表示一個可隱式轉換(§6.1)的值到switch語句的控制類型。 如果同一switch語句中的兩個或多個case標簽指定相同的常量值,則會發生編譯時錯誤。

考慮到這一點,即使你能實現這一點,它也會充滿代碼味道。 這樣做的主要原因是它違反了開放/封閉原則( http://www.oodesign.com/open-close-principle.html ),因為你必須修改這部分代碼作為新的類型U(無論它們是什么)被引入。 考慮這個不好的例子:

    public enum ItemType : int {

        Default = 0,

        Basic = 1,

        Advanced = 2,

        Expert = 3
    }

    public class NotSoGoodItem {

        public string Name { get; set; }

        public int Id { get; set; }

        public ItemType DataType { get; set; }

        public List<String> BasicSettings {get; set;}

        public List<String> AdvancedSettings {get; set;}

        public List<String> ExperSettings {get; set;}

    }

    public static NotSoGoodItem CreateNewItem(ItemType item) {
        //item is inherently an int
        switch (item) {
            case ItemType.Default | ItemType.Basic:
                return new NotSoGoodItem() { Name = "Basic Item", BasicSettings = new List<String>() };
            case ItemType.Advanced:
                return new NotSoGoodItem() { Name = "Advanced Item", AdvancedSettings = new List<String>() };
            case ItemType.Expert:
                return new NotSoGoodItem() { Name = "Expert Item", AdvancedSettings = new List<String>() };
            default:
                return null;
        }
    }

在這種情況下,switch語句使用枚舉值(繼承自int並滿足規范要求)來確定要填充的屬性以及要設置的值。 這是有問題的,因為如果你想引入一個新的級別或ItemType ,比如Guru,你將不得不修改至少3個代碼文件來進行這個改變(enum, NotSoGoodItem來添加屬性,以及執行創建)。

相反,通過使用泛型,可以使用以下良好示例簡化此代碼:

    public abstract class GoodItem {

        protected GoodItem() { }

        private string _name;

        public virtual string Name { get { return string.Concat(Prefix, _name); } set { _name = value; } }

        protected virtual string Prefix { get; set; }

        public virtual int Id { get; set; }

        public virtual List<String> Settings { get; set; }
    }

    public class BasicItem : GoodItem {

        public BasicItem()
            : base() {
                Prefix = "Basic";
        }
    }

    public class AdvancedItem : GoodItem {
        public AdvancedItem()
            : base() {
                Prefix = "Advanced";
        }
    }

    public class ExpertItem : GoodItem {
        public ExpertItem()
            : base() {
                Prefix = "Expert";
        }
    }
    public static T CreateNewItem<T>() where T : GoodItem {
        return Activator.CreateInstance<T>();
    }

使用此方法,必須修改零代碼文件。 所有需要做的就是添加一個繼承自GoodItem的新類型。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM