• C# 泛型中的数据类型判定与转换


    提到类型转换,首先要明确C#中的数据类型,主要分为值类型和引用类型:

    1.常用的值类型有:(struct)

    整型家族:int,byte,char,short,long等等一系列

    浮点家族:float,double,decimal

    孤独的枚举:enum

    孤独的布尔:bool

    2.常用的引用类型有:

    string,class,array,delegate,interface

    值得注意的是,无论是值类型还是引用类型,在C#中都派生于object,没错,这家伙就是万恶之源!

    正是因为有了这一特性,于是我们才能通过装箱和拆箱愉快地将这些数据类型在值类型,object,引用类型间反复横跳。

    当然了,无论是装箱和拆箱,对于性能都是有消耗的,不到万不得已的时候尽量不要用(虽然我才不管这些,只要我用的爽就行了233)

    虽然一般不提倡用object类型作为函数参数,取而代之使用泛型成为首选,那么如何判断泛型参数的具体数据类型并进行有效转换呢?

    比如下面的例子:

     1 [System.Serializable]
     2 public struct Property<T> where T : struct
     3 {
     4     public string Label { get; }
     5     public T Value { get; }
     6     public PropertyType Type { get; }
     7     public Property(string label, T value, PropertyType type = PropertyType.Sub)
     8     {
     9         Label = label;
    10         Value = value;
    11         Type = type;
    12     }
    13 
    14     public static Property<T> operator +(Property<T> a, Property<T> b)
    15     {
    16         var prop = new Property<T>();
    17         if (a.Label == b.Label && a.Type == b.Type)
    18         {
    19             //怎么知道这个值到底是int还是float...
    20         }
    21         return prop;
    22     }
    23 }
    1 public enum PropertyType
    2 {
    3     Main,
    4     Sub
    5 }

    定义了一个名叫「属性」的结构体,包含标签,具体值和属性类别(是主属性还是副属性),并使用泛型约束数据为值类型。

    现在想要快速对这个结构体进行加法操作,于是增加操作符重载函数,方便愉快的对两个属性的值相加,但问题是泛型是无法强转为任何一种非object数据类型,直接相加则更是不可能。

    这时就想到了以object类型作为桥梁,进行具体的类型判定与转换:

     1     public static Property<T> operator +(Property<T> a, Property<T> b)
     2     {
     3         if (a.Label == b.Label && a.Type == b.Type)
     4         {
     5             object tempa = a.Value;
     6             object tempb = b.Value;
     7 
     8             object add;
     9             if (tempa is int)
    10             {
    11                 add = (int)tempa + (int)tempb;
    12             }
    13             else if (tempa is float)
    14             {
    15                 add = (float)tempa + (float)tempb;
    16             }
    17             //...其他类型
    18             else
    19             {
    20                 return new Property<T>();
    21             }
    22 
    23             return new Property<T>(a.Label, (T)add, a.Type);
    24         }
    25         return new Property<T>();
    26     }

    判定类型时可以使用is关键字,也可直接取得值的类型或泛型类型进行判定:

    1             if (tempa.GetType() == typeof(float))
    2             {
    3 
    4             }
    5             //or
    6             if (typeof(T) == typeof(float))
    7             {
    8 
    9             }

    上面的方案虽然可以解决类型转换的需求,但频繁的拆箱和装箱以及类型判定对性能的还是有一定影响,而且如果每一种类型都写进if-else,看上去像千层塔一般难受。是时候轮到dynamic登场了。

    .Net 4.0 以后开始支持动态数据类型——也就是dynamic关键字;令人兴奋的是,dynamic可以被赋值为任何一种类型的值,当然也包括泛型。

    然而值得注意的是,dynamic关键字并不会在程序编译的时候进行校验,而只在运行时动态判定,所以使用的时需要格外小心。

    当然了,多次运行时的性能要远远高于装箱和拆箱,而且书写起来也是相当简洁美观(¯﹃¯):

     1     public static Property<T> operator +(Property<T> a, Property<T> b)
     2     {
     3         if (a.Label == b.Label && a.Type == b.Type)
     4         {
     5             dynamic x1 = a.Value;
     6             dynamic x2 = b.Value;
     7             return new Property<T>(a.Label, (T)(x1 + x2), a.Type);
     8         }
     9         return new Property<T>();
    10     }

    可以直接执行相加操作,但如果实际传入的两个数据类型并不能相加如bool,则会在运行时报错;当然了,如果想进一步防止安全,还可以增加更多的类型判定语句,如:

     1     public static Property<T> operator +(Property<T> a, Property<T> b)
     2     {
     3         if (a.Label == b.Label && a.Type == b.Type)
     4         {
     5             if (typeof(T) != typeof(bool) && typeof(T)!=typeof(Enum))
     6             {
     7                 dynamic x1 = a.Value;
     8                 dynamic x2 = b.Value;
     9                 return new Property<T>(a.Label, (T)(x1 + x2), a.Type);
    10             }
    11         }
    12         return new Property<T>();
    13     }

    补充一句,dynamic关键字在Unity中可能会报错,因为Unity默认用的是.Net Api为2.0版本,需要升级为4.0之后的版本才能使用该关键字,具体设置如下:

    下面做一个简单测试:

     1 using UnityEngine;
     2 
     3 public class MicrosoftCSharpTest : MonoBehaviour
     4 {
     5     void Start()
     6     {
     7         dynamic a = 5.1f;
     8         dynamic b = 3;
     9         Debug.Log(a + b);
    10 
    11         var hp1 = new Property<int>("Hp", 41);
    12         var hp2 = new Property<int>("Hp", 5);
    13         var hp = hp1 + hp2;
    14         Debug.Log(hp.Label + " : " + hp.Value);
    15 
    16         var miss1 = new Property<float>("MissRate", .1f);
    17         var miss2 = new Property<float>("MissRate", .05f);
    18         var miss = miss1 + miss2;
    19         Debug.Log(miss.Label + " : " + miss.Value);
    20     }
    21 }

  • 相关阅读:
    VUE集成keycloak和Layui集成keycloak
    iscsi基本命令
    Linux网卡bond模式
    Unmount and run xfs_repair
    Centos7 升级过内核 boot分区无法挂载修
    Centos7 误删除bin/sbin之类的恢复
    QSS 记录
    #pragma 小节
    解决Github打不开问题
    判断数据是否在指定区间内
  • 原文地址:https://www.cnblogs.com/koshio0219/p/13331278.html
Copyright © 2020-2023  润新知