1. 程式人生 > >C#進階之路(七)反射的應用

C#進階之路(七)反射的應用

 

反射在C#中的應用還是很多的,但它對程式碼的效能有一定影響。

反射的效能:

  使用反射來呼叫型別或者觸發方法,或者訪問一個欄位或者屬性時clr 需要做更多的工作:校驗引數,檢查許可權等等,所以速度是非常慢的。所以儘量不要使用反射進行程式設計,對於打算編寫一個動態構造型別(晚繫結)的應用程式,可以採取以下的幾種方式進行代替:

1、通過類的繼承關係。讓該型別從一個編譯時可知的基礎型別派生出來,在執行時生成該型別的一個例項,將對其的引用放到其基礎型別的一個變數中,然後呼叫該基礎型別的虛方法。

2、通過介面實現。在執行時,構建該型別的一個例項,將對其的引用放到其介面型別的一個變數中,然後呼叫該介面定義的虛方法。

3、通過委託實現。讓該型別實現一個方法,其名稱和原型都與一個在編譯時就已知的委託相符。在執行時先構造該型別的例項,然後在用該方法的物件及名稱構造出該委託的例項,接著通過委託呼叫你想要的方法。這個方法相對與前面兩個方法所作的工作要多一些,效率更低一些。

反射建立例項

  System.Activator提供了方法來根據型別動態建立物件,比如建立一個DataTable:

Type  t  =  Type.GetType("System.Data.DataTable,System.Data,Version=1.0.3300.0,  Culture=neutral,  PublicKeyToken=b77a5c561934e089
"); DataTable table = (DataTable)Activator.CreateInstance(t);

例二:根據有引數的構造器建立物件

namespace TestSpace
{
    public class TestClass
    {
        private string _value;
        public TestClass(string value)
        {
            _value = value;
        }
    }
}
…
Type  t  =  Type.GetType(“TestSpace.TestClass”);
Object[]  constructParms  
= new object[] {“hello”}; //構造器引數 TestClass obj = (TestClass)Activator.CreateInstance(t,constructParms); …

把引數按照順序放入一個Object陣列中即可

反射執行方法

//獲取型別資訊
Type t = Type.GetType("TestSpace.TestClass");
//構造器的引數
object[] constuctParms = new object[] { "timmy" };
//根據型別建立物件
object dObj = Activator.CreateInstance(t, constuctParms);
//獲取方法的資訊
MethodInfo method = t.GetMethod("GetValue");
//呼叫方法的一些標誌位,這裡的含義是Public並且是例項方法,這也是預設的值
BindingFlags flag = BindingFlags.Public | BindingFlags.Instance;
//GetValue方法的引數
object[] parameters = new object[] { "Hello" };
//呼叫方法,用一個object接收返回值
object returnValue = method.Invoke(dObj, flag, Type.DefaultBinder, parameters, null);

動態建立委託

  委託是C#中實現事件的基礎,有時候不可避免的要動態的建立委託,實際上委託也是一種型別:System.Delegate,所有的委託都是從這個類派生的

System.Delegate提供了一些靜態方法來動態建立一個委託,比如一個委託:

namespace TestSpace
{
    delegate string TestDelegate(string value);
    public class TestClass
    {
        public TestClass()
        {
        }
        public string GetValue(string value)
        {
            return value;
        }
    }
}

使用示例:

TestClass  obj  =  new  TestClass();
//獲取型別,實際上這裡也可以直接用typeof來獲取型別
Type  t  =  Type.GetType(“TestSpace.TestClass”);
//建立代理,傳入型別、建立代理的物件以及方法名稱
TestDelegate  method  =  (TestDelegate)Delegate.CreateDelegate(t,obj,”GetValue”);
String  returnValue  =  method(“hello”);

批量生成插入SQL

這裡要注意的是,你傳入的T模型與你資料庫中的模型要是相同的。

/// <summary>
/// 批量加入MYSQL資料庫
/// </summary>
/// <param name="list"></param>
/// <param name="connectionString"></param>
/// <param name="tableName"></param>
/// <returns></returns>
public static int InsertByList<T>(List<T> list, string connectionString, string tableName)
{
    int count = 0;
    if (list == null || list.Count <= 0) throw new Exception("List無任何資料");
    if (string.IsNullOrEmpty(tableName)) throw new Exception("新增失敗!請先設定插入的表名");
    // 構建INSERT語句
    var sb = new StringBuilder();
    sb.Append("Insert into " + tableName + "(");
    Type type = typeof(T);
    foreach (var item in type.GetProperties())
    {
        if (item.Name == "UpdateTime")
        {
            continue;
        }
        sb.Append(item.Name + ",");
    }
    sb.Remove(sb.ToString().LastIndexOf(','), 1);
    sb.Append(") VALUES ");
    foreach (var item in list)
    {
        sb.Append("(");
        foreach (var pi in type.GetProperties())
        {
            if (pi.Name == "UpdateTime")
            {
                continue;
            }

            if (pi.PropertyType.Name == "Nullable`1")//可空型別,判定是否為空
            {
                var aa = type.GetProperty(pi.Name)?.GetValue(item, null);
                if (aa == null)
                {
                    sb.Append("null,");
                }
                else
                {
                    sb.Append("'" + type.GetProperty(pi.Name)?.GetValue(item, null)?.ToString().Replace(@"\",@"\\").Replace("'", @"\'") + "',");
                }
            }
            else
            {
                sb.Append("'" + type.GetProperty(pi.Name)?.GetValue(item, null)?.ToString().Replace(@"\", @"\\").Replace("'", @"\'") + "',");
            }

        }
        sb.Remove(sb.ToString().LastIndexOf(','), 1);
        sb.Append("),");

    }
    sb.Remove(sb.ToString().LastIndexOf(','), 1);
    sb.Append(";");
    sb.Append("select @@IDENTITY");

    using (var con = new MySqlConnection(connectionString))
    {
        con.Open();
        using (var cmd = new MySqlCommand(sb.ToString(), con))
        {
            try
            {
                count = Convert.ToInt32(cmd.ExecuteScalar());
            }
            catch (Exception ex)
            {
                LogHelper.Error("批量sql插入操作失敗:" + ex.Message + "--" + ex.Source + "---" + ex.StackTrace);
            }
        }
    }
    return count;
}

判斷物件是否已賦值

這裡model是要驗證的例項

Type type  = model.GetType();
bool res= false;
//判斷例項是否是已經賦值, false 未賦值   true 已經賦值
foreach (var item in type.GetProperties())
{
    List<string> skipList=new List<string>{"UpdateTime","Id"};
    if (skipList.Contains(item.Name))
    {
        continue;
    }
    var typeThis =item.PropertyType;
    var valueDefault = typeThis.IsValueType ? Activator.CreateInstance(typeThis) : null;
    var gggggg = item.GetValue(model);

    if (valueDefault==null)
    {
        if (gggggg!=null)
        {
            res = true;
            break;
        }
    }
    //如果直接使用== 其實是對引用地址的對比
    else if (valueDefault.ToString() !=gggggg.ToString())
    {
        res = true;
        break;
    }
}