.net 反射訪問私有變量和私有方法 如何建立C# Closure ? C# 批量生成隨機密碼,必須包含數字和字母,並用加密算法加密 C#中的foreach和yield 數組爲何可使用linq查詢

如下爲本次實踐代碼:程序員

複製代碼
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleTest
{
    class Program
    {
        static void Main(string[] args)
        {
            //反射讀取類私有屬性
            Person per = new Person("ismallboy", "20102100104");
            Type t = per.GetType();
            //獲取私有方法
            MethodInfo method = t.GetMethod("GetStuInfo", BindingFlags.NonPublic | BindingFlags.Instance);
            //訪問無參數私有方法
            string strTest = method.Invoke(per, null).ToString();
            //訪問有參數私有方法
            MethodInfo method2 = t.GetMethod("GetValue", BindingFlags.NonPublic | BindingFlags.Instance);
            object[] par = new object[2];
            par[0] = "ismallboy";
            par[1] = 2;
            string strTest2 = method2.Invoke(per, par).ToString();

            //獲取私有字段
            PropertyInfo field = t.GetProperty("Name", BindingFlags.NonPublic | BindingFlags.Instance);
            //訪問私有字段值
            string value = field.GetValue(per).ToString();
            //設置私有字段值
            field.SetValue(per, "new Name");
            value = field.GetValue(per).ToString();
        }
    }

    /// <summary>
    /// 我的信息
    /// </summary>
    class Person
    {
        private string Name { get; set; }
        private string StuNo { get; set; }

        public Person(string name, string stuNo)
        {
            this.Name = name;
            this.StuNo = stuNo;
        }

        private string GetStuInfo()
        {
            return this.Name;
        }

        private string GetValue(string str, int n)
        {
            return str + n.ToString();
        }
    }
}
複製代碼

若是使用dynamic的話,也能夠以下:web

複製代碼
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleTest
{
    class Program
    {
        static void Main(string[] args)
        {
            //反射讀取類私有屬性
            dynamic per = new Person("ismallboy", "20102100104");
            Type t = per.GetType();
            //獲取私有方法
            MethodInfo method = t.GetMethod("GetStuInfo", BindingFlags.NonPublic | BindingFlags.Instance);
            //訪問無參數私有方法
            string strTest = method.Invoke(per, null);
            //訪問有參數私有方法
            MethodInfo method2 = t.GetMethod("GetValue", BindingFlags.NonPublic | BindingFlags.Instance);
            object[] par = new object[2];
            par[0] = "ismallboy";
            par[1] = 2;
            string strTest2 = method2.Invoke(per, par);

            //獲取私有字段
            PropertyInfo field = t.GetProperty("Name", BindingFlags.NonPublic | BindingFlags.Instance);
            //訪問私有字段值
            string value = field.GetValue(per);
            //設置私有字段值
            field.SetValue(per, "new Name");
            value = field.GetValue(per);
        }
    }

    /// <summary>
    /// 我的信息
    /// </summary>
    class Person
    {
        private string Name { get; set; }
        private string StuNo { get; set; }

        public Person(string name, string stuNo)
        {
            this.Name = name;
            this.StuNo = stuNo;
        }

        private string GetStuInfo()
        {
            return this.Name;
        }

        private string GetValue(string str, int n)
        {
            return str + n.ToString();
        }
    }
}
複製代碼

 

 

 

JavaScript中一個重要的概念就是閉包,閉包在JavaScript中有大量的應用,可是你知道麼?C#也能夠建立Closure。下面就介紹一下如何在C#中建立神奇的閉包。算法

  在這以前,咱們必須先知道如何在C#中定義函數express

  //函數定義,參數爲string,返回爲string
 Func<string, string> myFunc = delegate(string msg)
 {
       return "Msg:" + msg;
 };

利用Lambda表達式也能夠簡化上述的代碼,可是效果同樣:編程

  //Lambda
 Func<string, string> myFuncSame = msg => "Msg:" + msg;

定義好函數後,能夠進行調用:c#

  //函數調用
  string message= myFuncSame("Hello world");

定義一個帶外部變量(相對於內嵌函數而言)的嵌套函數,外部函數將內部嵌套的函數進行返回:windows

複製代碼
public static Func<int, int> Func()
 {
     var myVar = 1;
     Func<int, int> inc = delegate(int var1)
     {
         //myVar可以記錄上一次調用後的狀態(值)
         myVar = myVar + 1;
         return var1 + myVar;
     };
     return inc;
 }
複製代碼

C# Closure調用以下所示:數組

複製代碼
static void CsharpClosures()
 {
     var inc = Func();
     Console.WriteLine(inc(5));//7
     Console.WriteLine(inc(6));//9
 }
複製代碼

當第二次調用inc(6)時,此時函數內變量myVar並未像第一次調用函數時進行從新初始化(var myVar=1),而是保留了第一次運算的值,即 2,所以inc(6)返回的結果爲(2+1+6)=9.瀏覽器

 

要求:密碼必須包含數字和字母安全

思路:1.列出數字和字符。 組成字符串 :chars

        2.利用randrom.Next(int i)返回一個小於所指定最大值的非負隨機數。

        3. 隨機取不小於chars長度的隨機數a,取字符串chars的第a位字符。

        4.循環 8次,獲得8位密碼

        5.循環N次,批量獲得密碼。

代碼實現以下 Main函數:

複製代碼
static void Main(string[] args)
        {
            string chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
            Random randrom = new Random((int)DateTime.Now.Ticks);
            string path1 = @"C:\Users\lenovo\Desktop\pws.txt";
            for (int j = 0; j < 10000;j++ )
            {
                string str = "";
                for (int i = 0; i < 8; i++)
                {
                    str += chars[randrom.Next(chars.Length)];//randrom.Next(int i)返回一個小於所指定最大值的非負隨機數
                }
                if (IsNumber(str))//判斷是否全是數字
                    continue;
                if (IsLetter(str))//判斷是否全是字母
                    continue;
                File.AppendAllText(path1, str);
                string pws = Md5(str,32);//MD5加密
                File.AppendAllText(path1, "," + pws + "\r\n");
            }
            Console.WriteLine("ok");
            Console.Read();
        }
複製代碼

巧用String.trim 函數,判斷是否全是數字,全是字母。

說明:string.trim   從 String 對象移除前導空白字符和尾隨空白字符。

返回:一個字符串副本,其中從該字符串的開頭和末尾移除了全部空白字符。

有一個重載:string.Trim(params char[] trimChars)   

//從當前System.string對象移除數組中指定的一組字符的全部前導匹配項和尾部匹配項

 trimChars:要刪除的字符數組

方法實現以下代碼:

複製代碼
        //判斷是否全是數字
        static bool IsNumber(string str)
        {
            if (str.Trim("0123456789".ToCharArray()) == "")
                return true;
            return false;
        }
        //判斷是否全是字母
        static bool IsLetter(string str)
        {
           if (str.Trim("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".ToCharArray()) == "")
               return true;
           return false;
        }
複製代碼

 用MD5加密,算法代碼實現以下:

複製代碼
/// <summary>
        /// MD5加密
        /// </summary>
        /// <param name="str">加密字元</param>
        /// <param name="code">加密位數16/32</param>
        /// <returns></returns>
        public static string Md5(string str, int code)
        {
            string strEncrypt = string.Empty;

            MD5 md5 = new MD5CryptoServiceProvider();
            byte[] fromData = Encoding.GetEncoding("GB2312").GetBytes(str);
            byte[] targetData = md5.ComputeHash(fromData);
            for (int i = 0; i < targetData.Length; i++)
            {
                strEncrypt += targetData[i].ToString("X2");
            }
            if (code == 16)
            {
                strEncrypt = strEncrypt.Substring(8, 16);
            }
            return strEncrypt;
        }
複製代碼

生成批量密碼,和加密後的密碼以下圖:

 

1. foreach

C#編譯器會把foreach語句轉換爲IEnumerable接口的方法和屬性。

foreach (Person p in persons)
 {
     Console.WriteLine(p);
 }

foreach語句會解析爲下面的代碼段。

調用GetEnumerator()方法,得到數組的一個枚舉

在while循環中,只要MoveNext()返回true,就一直循環下去

用Current屬性訪問數組中的元素

複製代碼
IEnumerator enumerator = persons. GetEnumerator();
 while (enumerator.MoveNext())
 {
    Person p = (Person) enumerator.Current;
    Console.WriteLine(p);
}
複製代碼

2. yield語句

yield語句的兩種形式:

yield return <expression>;
yield break;

使用一個yield return語句返回集合的一個元素

包含yield語句的方法或屬性是迭代器。迭代器必須知足如下要求

a. 返回類型必須是IEnumerableIEnumerable<T>IEnumerator或 IEnumerator<T>

b. 它不能有任何ref或out參數

yield return語句不能位於try-catch快。yield return語句能夠位於try-finally的try塊

複製代碼
try
              {
                  // ERROR: Cannot yield a value in the boday of a try block with a catch clause
                 yield return "test";
              }
             catch
             { }
 
              try
             {
                 // 
                 yield return "test again";
             }
             finally
             { }
 
             try
             { }
             finally
             { 
                 // ERROR: Cannot yield in the body of a finally clause
                yield return ""; 
             }
複製代碼

yield break語句能夠位於try塊或catch塊,可是不能位於finally塊

下面的例子是用yield return語句實現一個簡單集合的代碼,以及用foreach語句迭代集合

複製代碼
using System;
using System.Collections.Generic;

namespace ConsoleApplication6
{
    class Program
    {
        static void Main(string[] args)
        {
            HelloCollection helloCollection = new HelloCollection();
            foreach (string s in helloCollection)
            {
                Console.WriteLine(s);
                Console.ReadLine();
            }
        }
    }

    public class HelloCollection
    {
        
        public IEnumerator<String> GetEnumerator()
        {
            // yield return語句返回集合的一個元素,並移動到下一個元素上;yield break能夠中止迭代
            yield return "Hello";
            yield return "World";
        }
    }
}
複製代碼

使用yield return語句實現以不一樣方式迭代集合的類:

複製代碼
using System;
using System.Collections.Generic;

namespace ConsoleApplication8
{
    class Program
    {
        static void Main(string[] args)
        {
            MusicTitles titles = new MusicTitles();
            foreach (string title in titles)
            {
                Console.WriteLine(title);
            }
            Console.WriteLine();

            foreach (string title in titles.Reverse())
            {
                Console.WriteLine(title);
            }
            Console.WriteLine();

            foreach (string title in titles.Subset(2, 2))
            {
                Console.WriteLine(title);
                Console.ReadLine();
            }
        }
    }

    public class MusicTitles
    {
        string[] names = { "a", "b", "c", "d" };
        public IEnumerator<string> GetEnumerator()
        {
            for (int i = 0; i < 4; i++)
            {
                yield return names[i];
            }
        }

        public IEnumerable<string> Reverse()
        {
            for (int i = 3; i >= 0; i--)
            {
                yield return names[i];
            }
        }

        public IEnumerable<string> Subset(int index, int length)
        {
            for (int i = index; i < index + length; i++)
            {
                yield return names[i];
            }
        }
    }
}
複製代碼

 

 

問題引出

  這視乎是個徹底沒必要要進行討論的話題,由於linq(這裏具體是linq to objects)原本就是針對集合類型的,數組類型做爲集合類型的一種固然可使用了。不過我仍是想寫一下,這個問題源於qq羣裏一位朋友的提問:.net的數組類型都隱式繼承了Array類,該類是一個抽象類,而且實現了IEnumerable、ICollection、IList接口。但linq的方法都是針對實現了IEnumerable<T>泛型接口的,Array類並無實現這些泛型接口,爲何可使用這些方法呢?

  linq to objects的本質是經過擴展方法來實現集合的查詢,這些擴展方法定義在一個Enumerable的靜態類中。Enumerable類下的全部擴展方法的第一個參數都是IEnumerable<T> 類型,表示它能夠經過IEnumerable<T>類型進行調用。

淺析數組類型

1. 全部數組類型都隱式派生自Array

  當咱們定義一個FileStream[] 數組時,CLR會爲當前的AppDomain建立一個FileStream[] 類型,該類型派生自 Array。因此數組是引用類型,在堆中分配內存空間。Array類是一個抽象類,定義了許多關於經常使用的實例方法和靜態方法,供全部的數組類型使用。例如常見的:Length屬性,CopyTo方法等等。

2. 全部的數組類型都隱式實現了IEnumerable<T>接口

  就如上面所所的,這是一個理所固然的問題,爲了提升開發效率,數組類型理應可使用linq進行查詢。但因爲數組能夠是多維數組或者非0基數組,因此Array類並無實現IEnumerable<T>、ICollection<T>、IList<T> 這幾個泛型接口,而只是實現了非泛型版本的。實際上,CLR會自動爲一維的數組類型實現這些泛型接口(指定T類型參數的具體類型),而且還會爲它們的父類實現。例如咱們定義一個FileStream[] 數組類型,那麼CLR會爲咱們建立以下的層次類型結構:

                                

因爲CLR的隱式實現,才使咱們能夠將一維數組類型應用在須要IEnumerable<T>泛型接口的地方。

  按照上面的說法,咱們能夠將FileStream[] 類型的對象傳遞給以下的方法:

  void F1(IEnumerable<object> oEnumerable);

  void F2(ICollection<Stream> sCollection);

  void F3(IList<FileStream> fList);

  這是對於引用類型而言的,若是是值類型,則不爲會它的基類實現這些接口。例如DateTimel類型(基類包括ValueType和Object),DateTime[]數組類型不能傳遞給上面的F1方法,這是由於值類型的數組的內存佈局與引用類型的數組不一樣。

 

具名參數 和 可選參數 是 C# framework 4.0 出來的新特性。

一. 常規方法定義及調用

複製代碼
public void Demo1(string x, int y)
{ 
      //do something...
}


public void Main()
{
      //調用
       Demo1("similar", 22);
}
複製代碼

調用時,參數順序(類型)必須與聲明一致,且不可省略。

 

 

二. 可選參數的聲明及調用

可選參數分爲兩種狀況: 1. 部分參數可選;   2. 所有參數都是可選

複製代碼
//部分可選(x爲必選,y爲可選)
public void Demo2(string x, int y = 5)
{
      //do something...
}


public void Main()
{
       //調用
       Demo2("similar");       // y不傳入實參時,y使用默認值5
       Demo2("similar", 10);   // y傳入實參,則使用實參10
}
複製代碼

注: 當參數爲部分可選時, 可選參數  的聲明必須定義在 不可選參數 的後面(如上: y 的聲明在 x 以後),否則會出現以下錯誤提示:

複製代碼
//所有可選(x,y 均爲可選參數)
public void Demo3(string x = "demo", int y = 5)
{
       //do something...
}

public void Main()
{
       //調用
       Demo3();               // x,y不傳入實參時,x,y使用默認值 "demo",5
       Demo3("similar");      // y不傳入實參時,y使用默認值5
       Demo3("similar", 10);  // x,y都傳入實參
}
複製代碼

分前後。

       b. 參數聲明定義能夠無順序,但調用時必須與聲明時的一致。

上面的調用只寫的3種,其實還有一種,就是 x 使用默認值,y 傳入實參,即 :  Demo3(10);

但這樣調用會報錯,由於Demo3的第一個參數是 string 類型,錯誤消息如圖:

可是如今我只想傳入y, 不想傳入 x ,該怎麼辦呢,那麼就要用到 C#的 具名參數。

 

 

三. 具名參數

具名參數的使用主要是體如今函數調用的時候。

複製代碼
public void Main()
{
       //調用
       Demo3();                // x,y不傳入實參時,x,y使用默認值 "demo",5
       Demo3("similar");       // y不傳入實參時,y使用默認值5
       Demo3("similar", 10);   // x,y都傳入實參


       // 具名參數的使用
       Demo3(y:10);
}
複製代碼

經過具名參數,咱們能夠指定特定參數的值,這裏咱們經過 Demo3(y:10)就能夠解決咱們上面遇到的問題(x使用默認值,y使用實參)。

 

注: 當使用 具名參數時,調用方法能夠不用管參數的聲明順序,即以下調用方式也是能夠的:

在調用含有可選參數的方法時,vs中會有智能提示,提示哪些是能夠選參數及其默認值,中括號表示可選[]:

 

具名參數 和 可選參數 是 C# framework 4.0 出來的新特性。

一. 常規方法定義及調用

複製代碼
public void Demo1(string x, int y)
{ 
      //do something...
}


public void Main()
{
      //調用
       Demo1("similar", 22);
}
複製代碼

調用時,參數順序(類型)必須與聲明一致,且不可省略。

 

 

二. 可選參數的聲明及調用

可選參數分爲兩種狀況: 1. 部分參數可選;   2. 所有參數都是可選

複製代碼
//部分可選(x爲必選,y爲可選)
public void Demo2(string x, int y = 5)
{
      //do something...
}


public void Main()
{
       //調用
       Demo2("similar");       // y不傳入實參時,y使用默認值5
       Demo2("similar", 10);   // y傳入實參,則使用實參10
}
複製代碼

注: 當參數爲部分可選時, 可選參數  的聲明必須定義在 不可選參數 的後面(如上: y 的聲明在 x 以後),否則會出現以下錯誤提示:

複製代碼
//所有可選(x,y 均爲可選參數)
public void Demo3(string x = "demo", int y = 5)
{
       //do something...
}

public void Main()
{
       //調用
       Demo3();               // x,y不傳入實參時,x,y使用默認值 "demo",5
       Demo3("similar");      // y不傳入實參時,y使用默認值5
       Demo3("similar", 10);  // x,y都傳入實參
}
複製代碼

分前後。

       b. 參數聲明定義能夠無順序,但調用時必須與聲明時的一致。

上面的調用只寫的3種,其實還有一種,就是 x 使用默認值,y 傳入實參,即 :  Demo3(10);

但這樣調用會報錯,由於Demo3的第一個參數是 string 類型,錯誤消息如圖:

可是如今我只想傳入y, 不想傳入 x ,該怎麼辦呢,那麼就要用到 C#的 具名參數。

 

 

三. 具名參數

具名參數的使用主要是體如今函數調用的時候。

複製代碼
public void Main()
{
       //調用
       Demo3();                // x,y不傳入實參時,x,y使用默認值 "demo",5
       Demo3("similar");       // y不傳入實參時,y使用默認值5
       Demo3("similar", 10);   // x,y都傳入實參


       // 具名參數的使用
       Demo3(y:10);
}
複製代碼

經過具名參數,咱們能夠指定特定參數的值,這裏咱們經過 Demo3(y:10)就能夠解決咱們上面遇到的問題(x使用默認值,y使用實參)。

 

注: 當使用 具名參數時,調用方法能夠不用管參數的聲明順序,即以下調用方式也是能夠的:

在調用含有可選參數的方法時,vs中會有智能提示,提示哪些是能夠選參數及其默認值,中括號表示可選[]:

 

接口定義了一系列的行爲規範,爲類型定義一種Can-Do的功能。例如,實現IEnumerable接口定義了GetEnumerator方法,用於獲取一個枚舉數,該枚舉數支持在集合上進行迭代,也就是咱們常說的foreach。接口只是定義行爲,具體的實現須要由具體類型負責,實現接口的方法又分爲隱式實現與顯示實現

1、隱式/顯示實現接口方法

  簡單的說,咱們平時「默認」使用的都是隱式的實現方式。例如:

複製代碼
interface ILog
{
    void Log();
}
 
public class FileLogger : ILog
{
    public void Log()
    {
        Console.WriteLine("記錄到文件!");
    }
}
複製代碼

隱式實現很簡單,一般咱們約定接口命名以 I 開頭,方便閱讀。接口內的方法不須要用public,編譯器會自動加上。類型中實現接口的方法只能是public,也能夠定義成虛方法,由子類重寫。如今看顯示實現的方式:

複製代碼
public class EventLogger : ILog
{
    void ILog.Log()
    {
        Console.WriteLine("記錄到系統事件!");
    }
}
複製代碼

與上面不一樣的是,方法用了ILog指明,並且沒有(也不能有)public或者private修飾符。

  除了語法上的不一樣,調用方式也不一樣,顯示實現只能用接口類型的變量來調用,如:

複製代碼
FileLogger fileLogger = new FileLogger();
fileLogger.Log(); //正確
EventLogger eventLogger = new EventLogger();           
eventLogger.Log(); //報錯
ILog log = new EventLogger();
log.Log(); //正確
複製代碼

2、什麼時候使用

  1. c#容許實現多個接口,若是多個接口定義了相同的方法,能夠用顯示實現的方式加以區分,例如:

複製代碼
 
public class EmailLogger : ILog, ISendable
{
    void ILog.Log()
    {
        Console.WriteLine("ILog");
    }
 
    void ISendable.Log()
    {
        Console.WriteLine("ISendable");
    }
}
複製代碼

 2. 加強編譯時的類型安全和避免值類型裝箱

  有了泛型,咱們天然能夠作到編譯時的類型安全和避免值類型裝箱的操做。但有時候可能沒有對應的泛型版本。例如:IComparable(這裏只是舉例,實際有IComparable<T>)。如:

複製代碼
interface IComparable
{
    int CompareTo(object obj);
}
 
struct ValueType : IComparable
{
    private int x;
    public ValueType(int x)
    {
        this.x = x;
    }
 
    public int CompareTo(object obj)
    {
        return this.x - ((ValueType)obj).x;
    }
} 

   //調用:
    
ValueType vt1 = new ValueType(1);
ValueType vt2 = new ValueType(2);
Console.WriteLine(vt1.CompareTo(vt2)); 
複製代碼

因爲形參是object,上面的CompareTo會發生裝箱;並且沒法得到編譯時的類型安全,例如咱們能夠隨便傳一個string,編譯不會報錯,等到運行時才拋出InvalidCastException。使用顯示實現接口的方式,如:

複製代碼
public int CompareTo(ValueType vt)
{
    return this.x - vt.x;
}
 
int IComparable.CompareTo(object obj)
{
    return CompareTo((ValueType)obj);
}  
複製代碼

再次執行上面的代碼,就不會發生裝箱操做,並且能夠得到編譯時的類型安全了。可是若是咱們用接口變量調用,就會再次發生裝箱並喪失編譯時的類型安全檢測能力

IComparable vt1 = new ValueType(1); //裝箱
ValueType vt2 = new ValueType(2);
Console.WriteLine(vt1.CompareTo(vt2)); //再次裝箱

3、缺點

  1. 顯示實現只能用接口類型變量調用,會給人的感受是某類型實現了該接口卻沒法調用接口中的方法。特別是寫成類庫給別人調用時,顯示實現的接口方法在vs中按f12都不會顯示出來。(這點有人在csdn提問過,爲何某個類型能夠不用實現接口方法)

  2. 對於值類型,要調用顯示實現的方法,會發生裝箱操做。

  3. 沒法被子類繼承使用。

 

對異步CTP感興趣有不少緣由。異步CTP使異步編程比之前更加容易了。它雖然沒有Rx強大,可是更容易學。異步CTP介紹了兩個新的關鍵字,async和await。異步方法(或Lambda表達式)必須返回void,Task或Task<TResult>。這篇文章不是介紹異步CTP的,由於網上有不少這樣的文章。這篇文章的目的是把程序員開始使用Async CTP遇到的一些常見問題集中起來。

推斷返回類型

當從異步方法返回一個值的時候,此方法體直接返回這個值,但該方法自己被聲明爲返回一個Task<TResult>。當聲明一個返回甲類型的方法卻必須返回一個乙類型時,就有點「斷連」了。

複製代碼
// 實際語法
public async Task<int> GetValue()
{
  await TaskEx.Delay(100);
  return 13; //返回類型是 "int", 而不是"Task<int>"
}
複製代碼

問題來了:爲何我不能這麼寫?

複製代碼
// 假想語法
public async int GetValue()
{
  await TaskEx.Delay(100);
  return 13; // 返回類型是 "int"
}
複製代碼
思考:該方法如何如何照顧調用者呢?異步方法必須返回一個實際結果類型Task<TResult>的值。所以,GetValue方法會出現返回Task<TResult>的智能提示(在對象瀏覽器,Reflector等中也是這樣的)。
 
在設計之初,推斷返回類型已經被考慮到了,但該設計團隊已經推斷出在異步方法中保持這種「斷連」比在代碼基上擴大這種「斷連」更好。現在這種「斷連」仍存在,但比之前更小了。該設計團隊的共識是一致的方法簽名更佳。
思考:async void 和async Task有什麼區別?
一個async Task方法就像任何其餘的異步操做同樣,只是沒有返回值。一個async void方法扮演一種高級操做。async Task方法可能被組合進其餘使用using await的異步方法。async void方法可能被用做一個事件句柄。async void方法也有其餘重要的屬性:在ASP.NET上下文中,它通知web服務器直到它返回,頁面才完成。
 
推斷返回類型會移除async void 和async Task間的區別:要麼全部的異步方法是async void(阻止可組合性),要麼都是async Task(阻止它們來自事件句柄,同時對ASP.NET要有一個可選擇的方案)。

 異步返回

 
在方法聲明返回類型和方法體返回的類型之間仍有「斷連」。該設計團隊的另外一個建議是:給return添加一個關鍵字來指示return返回的值,但這個也確實沒有返回什麼,以下所示:
複製代碼
// 假想語法
public async Task<int> GetValue()
{
  await TaskEx.Delay(100);
  async return 13; // "async return" 意味着值被包裝在Task中
}
複製代碼
思考:將大量的代碼從同步轉爲異步。

async return關鍵字也被考慮到了,但並無足夠的說服力。當把一些同步代碼轉成異步代碼時,這尤爲正確。強制人們給每一個return語句添加asynchronous就好像是「沒必要要的忙碌」。比較而言,習慣於「斷連」更容易。

推斷「async」

async關鍵字必須用在使用了await關鍵字的方法上。然而,若是把async用在了一個沒有使用await的方法上,也會收到一個警告。

問題:爲何async不能根據await的存在推斷出來?

複製代碼
//假想語法
public Task<int> GetValue()
{
  // "await" 的存在暗示這是一個 "async" 方法.
  await TaskEx.Delay(100);
  return 13;
}
複製代碼

思考:向後兼容性和代碼可讀性

單字的await關鍵字具備太大的打破變化。在異步方法上的多字await(如await for)或一個關鍵字之間的選擇,只是在那個方法內部啓用await關鍵字。很明顯,使用async標記方法讓人類和計算機分析起來更容易,所以設計團隊決定使用async/await對。

推斷「await」

問題:既然顯示包括async有意義(看上面),爲何await不能根據async的存在推斷出來呢?

複製代碼
// 假想語法
public async Task<int> GetValue()
{
  // 暗示有"await",由於這是一個 "async" 方法.
  TaskEx.Delay(100);
  return 13;
}
複製代碼

思考:異步操做的並行組合。

乍一看,推斷await推斷彷佛簡化了基本的異步操做。只要全部的等待能夠按序列(如一個操做等待,而後另外一個,再而後另外一個)完成,這個就能很好的工做。然而,當有人考慮並行組合的時候,它崩潰了。

異步CTP中的並行組合使用TaskEx.WhenAny 和TaskEx.WhenAll方法。這有一個簡單的例子,這個方法當即開始了兩個操做,而且等待它們完成。

複製代碼
// 實際語法
public async Task<int> GetValue()
{
  // 異步檢索兩個部分的值
  // 注意此時它們是沒有等待的「not await」
  Task<int> part1 = GetValuePart1();
  Task<int> part2 = GetValuePart2();

  // 等待它們的值到達。
  await TaskEx.WhenAll(part1, part2);

  // 計算咱們的結果
  int value1 = await part1; // 實際上沒有等待
  int value2 = await part2; //實際上沒有等待
  return value1 + value2;
}
複製代碼

爲了處理並行組合,咱們必須有能力說咱們將不會await一個表達式。

 

 

1、建立線程

  在整個系列文章中,咱們主要使用Visual Studio 2015做爲線程編程的主要工具。在C#語言中建立、使用線程只須要按如下步驟編寫便可:

一、啓動Visual Studio 2016,新建一個控制檯應用程序。

二、確保該控制檯程序使用.NET Framework 4.6或以上版本。然而在該篇中的全部示例使用較低版本能夠正常工做。

三、雙擊打開該控制檯應用程序中的「Program.cs」文件,在其中編寫以下代碼:

複製代碼
using System;
  using System.Threading;
  using static System.Console;
  
  namespace Recipe01
  {
      class Program
      {
          static void PrintNumbers()
         {
             WriteLine("Starting...");
             for (int i = 1; i < 10; i++)
             {
                 WriteLine(i);
             }
         }
 
         static void Main(string[] args)
         {
            Thread t = new Thread(PrintNumbers);
             t.Start();
             PrintNumbers();
         }
     }
 }
複製代碼

  在第2行代碼處,咱們導入了System.Threading命名空間,該命名空間包含了咱們編寫多線程程序所須要的全部類型。

  在第3行代碼處,咱們使用了C# 6.0的using static特性,使用了該特性以後,在代碼中容許咱們在使用System.Console類型的靜態方法的時候不須要指定其類型名。

  在第9~16行代碼處,咱們定義了一個名爲「PrintNumbers」的方法,該方法將在「Main」方法和線程中進行調用。

  在第20行代碼處,咱們建立了一個線程來運行「PrintNumbers」方法,當咱們初始化一個線程時,一個「ThreadStart」或「ParameterizedThreadStart」委託的實例被傳遞給線程的構造方法。

  在第21行代碼處,咱們啓動線程。

  在第22行代碼處,咱們在「Main」方法中調用「PrintNumbers」方法。

四、運行該控制檯應用程序,運行效果(每次運行效果可能不一樣)以下圖所示:

2、停止線程

   在這一節,咱們將讓線程等待一些時間,在等待的這段時間內,該線程不會消耗操做系統的資源。編寫步驟以下:

一、使用Visual Studio 2015建立一個新的控制檯應用程序。

二、雙擊打開「Program.cs」文件,編寫代碼以下所示:

複製代碼
using System;
  using System.Threading;
  using static System.Console;
  
  namespace Recipe01
  {
      class Program
      {
          static void PrintNumbers()
        {
             WriteLine("Starting...");
             for (int i = 1; i < 10; i++)
             {
                 WriteLine(i);
             }
         }
 
         static void Main(string[] args)
         {
             Thread t = new Thread(PrintNumbers);
             t.Start();
             PrintNumbers();
         }
     }
 }
複製代碼

三、運行該控制檯應用程序,運行效果(每次運行效果可能不一樣)以下圖所示:

3、線程等待

   在這一節中,咱們將講述如何在一個線程執行完畢後,再執行剩餘的代碼,要完成這個工做,咱們不能使用Thread.Sleep方法,由於咱們不知道另外一個線程精確的執行時間。要使一個線程等待另外一個線程執行完畢後再進行其餘工做,只須要按下列步驟編寫代碼便可:

一、使用Visual Studio 2015建立一個新的控制檯應用程序。

二、雙擊打開「Program.cs」文件,編寫以下代碼:

複製代碼
using System;
  using System.Threading;
  using static System.Console;
  using static System.Threading.Thread;
  
  namespace Recipe03
  {
      class Program
      {
        static void PrintNumbersWithDelay()
         {
             WriteLine("Starting...");
 
           for(int i = 1; i < 10; i++)
             {
                 Sleep(TimeSpan.FromSeconds(2));
                 WriteLine(i);
             }
         }

         static void Main(string[] args)
        {
             WriteLine("Starting...");
           Thread t = new Thread(PrintNumbersWithDelay);
          t.Start();
             t.Join();
             WriteLine("Thread completed");
         }
    }
}
複製代碼

三、運行該控制檯應用程序,運行效果以下圖所示:

 在第26行代碼處,咱們在「Main」方法中調用調用「t.Join」方法,該方法容許咱們等待線程t執行完畢後,再執行「Main」方法中剩餘的代碼。有了該技術,咱們能夠同步兩個線程的執行步驟。第一個線程等待第二個線程執行完畢後,再進行其餘的工做,在第一個線程等待期間,第一個線程的狀態爲「bolcked」狀態,和咱們調用Thread.Sleep的狀態同樣。

4、終止線程

  在這一節中,咱們將講述如何終止另外一個線程的執行。步驟以下:

一、使用Visual Studio 2015建立一個新的控制檯應用程序。

二、雙擊打開「Program.cs」文件,編寫以下代碼:

複製代碼
 using System;
 using System.Threading;
  using static System.Console;
  using static System.Threading.Thread;
  
  namespace Recipe04
  {
      class Program
      {
         static void PrintNumbers()
         {
             WriteLine("Starting...");
 
             for (int i = 1; i < 10; i++)
             {
                 WriteLine(i);
             }
         }
 
         static void PrintNumbersWithDelay()
         {
             WriteLine("Starting...");
             for (int i = 1; i < 10; i++)
             {
                 Sleep(TimeSpan.FromSeconds(2));
                 WriteLine(i);
             }
         }
 
         static void Main(string[] args)
         {
             WriteLine("Starting program...");
             Thread t = new Thread(PrintNumbersWithDelay);
             t.Start();
             Thread.Sleep(TimeSpan.FromSeconds(6));
             t.Abort();
             WriteLine("A thread has been aborted");
             t = new Thread(PrintNumbers);
             t.Start();
             PrintNumbers();
         }
     }
 }
複製代碼

三、運行該控制檯應用程序,運行效果(每次運行效果可能不一樣)以下圖所示:

 

 

在對Bitmap圖片操做的時候,有時須要用到獲取或設置像素顏色方法:GetPixel 和 SetPixel,

若是直接對這兩個方法進行操做的話速度很慢,這裏咱們能夠經過把數據提取出來操做,而後操做完在複製回去能夠加快訪問速度

其實對Bitmap的訪問還有兩種方式,一種是內存法,一種是指針法

一、內存法

  這裏定義一個類LockBitmap,經過把Bitmap數據拷貝出來,在內存上直接操做,操做完成後在拷貝到Bitmap中

複製代碼
public class LockBitmap
        {
            Bitmap source = null;
            IntPtr Iptr = IntPtr.Zero;
            BitmapData bitmapData = null;

            public byte[] Pixels { get; set; }
            public int Depth { get; private set; }
            public int Width { get; private set; }
            public int Height { get; private set; }

            public LockBitmap(Bitmap source)
            {
                this.source = source;
            }

            /// <summary>
            /// Lock bitmap data
            /// </summary>
            public void LockBits()
            {
                try
                {
                    // Get width and height of bitmap
                    Width = source.Width;
                    Height = source.Height;

                    // get total locked pixels count
                    int PixelCount = Width * Height;

                    // Create rectangle to lock
                    Rectangle rect = new Rectangle(0, 0, Width, Height);

                    // get source bitmap pixel format size
                    Depth = System.Drawing.Bitmap.GetPixelFormatSize(source.PixelFormat);

                    // Check if bpp (Bits Per Pixel) is 8, 24, or 32
                    if (Depth != 8 && Depth != 24 && Depth != 32)
                    {
                        throw new ArgumentException("Only 8, 24 and 32 bpp images are supported.");
                    }

                    // Lock bitmap and return bitmap data
                    bitmapData = source.LockBits(rect, ImageLockMode.ReadWrite,
                                                 source.PixelFormat);

                    // create byte array to copy pixel values
                    int step = Depth / 8;
                    Pixels = new byte[PixelCount * step];
                    Iptr = bitmapData.Scan0;

                    // Copy data from pointer to array
                    Marshal.Copy(Iptr, Pixels, 0, Pixels.Length);
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }

            /// <summary>
            /// Unlock bitmap data
            /// </summary>
            public void UnlockBits()
            {
                try
                {
                    // Copy data from byte array to pointer
                    Marshal.Copy(Pixels, 0, Iptr, Pixels.Length);

                    // Unlock bitmap data
                    source.UnlockBits(bitmapData);
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }

            /// <summary>
            /// Get the color of the specified pixel
            /// </summary>
            /// <param name="x"></param>
            /// <param name="y"></param>
            /// <returns></returns>
            public Color GetPixel(int x, int y)
            {
                Color clr = Color.Empty;

                // Get color components count
                int cCount = Depth / 8;

                // Get start index of the specified pixel
                int i = ((y * Width) + x) * cCount;

                if (i > Pixels.Length - cCount)
                    throw new IndexOutOfRangeException();

                if (Depth == 32) // For 32 bpp get Red, Green, Blue and Alpha
                {
                    byte b = Pixels[i];
                    byte g = Pixels[i + 1];
                    byte r = Pixels[i + 2];
                    byte a = Pixels[i + 3]; // a
                    clr = Color.FromArgb(a, r, g, b);
                }
                if (Depth == 24) // For 24 bpp get Red, Green and Blue
                {
                    byte b = Pixels[i];
                    byte g = Pixels[i + 1];
                    byte r = Pixels[i + 2];
                    clr = Color.FromArgb(r, g, b);
                }
                if (Depth == 8)
                // For 8 bpp get color value (Red, Green and Blue values are the same)
                {
                    byte c = Pixels[i];
                    clr = Color.FromArgb(c, c, c);
                }
                return clr;
            }

            /// <summary>
            /// Set the color of the specified pixel
            /// </summary>
            /// <param name="x"></param>
            /// <param name="y"></param>
            /// <param name="color"></param>
            public void SetPixel(int x, int y, Color color)
            {
                // Get color components count
                int cCount = Depth / 8;

                // Get start index of the specified pixel
                int i = ((y * Width) + x) * cCount;

                if (Depth == 32) // For 32 bpp set Red, Green, Blue and Alpha
                {
                    Pixels[i] = color.B;
                    Pixels[i + 1] = color.G;
                    Pixels[i + 2] = color.R;
                    Pixels[i + 3] = color.A;
                }
                if (Depth == 24) // For 24 bpp set Red, Green and Blue
                {
                    Pixels[i] = color.B;
                    Pixels[i + 1] = color.G;
                    Pixels[i + 2] = color.R;
                }
                if (Depth == 8)
                // For 8 bpp set color value (Red, Green and Blue values are the same)
                {
                    Pixels[i] = color.B;
                }
            }
        }
複製代碼

使用:先鎖定Bitmap,而後經過Pixels操做顏色對象,最後釋放鎖,把數據更新到Bitmap中

複製代碼
string file = @"C:\test.jpg";
            Bitmap bmp = new Bitmap(Image.FromFile(file));
            
            LockBitmap lockbmp = new LockBitmap(bmp);
            //鎖定Bitmap,經過Pixel訪問顏色
            lockbmp.LockBits();

            //獲取顏色
            Color color = lockbmp.GetPixel(10, 10);

            //從內存解鎖Bitmap
            lockbmp.UnlockBits();
複製代碼

二、指針法

  這種方法訪問速度比內存法更快,直接經過指針對內存進行操做,不須要進行拷貝,可是在C#中直接經過指針操做內存是不安全的,因此須要在代碼中加入unsafe關鍵字,在生成選項中把容許不安全代碼勾上,才能編譯經過

  這裏定義成PointerBitmap類

複製代碼
public class PointBitmap
            {
                Bitmap source = null;
                IntPtr Iptr = IntPtr.Zero;
                BitmapData bitmapData = null;

                public int Depth { get; private set; }
                public int Width { get; private set; }
                public int Height { get; private set; }

                public PointBitmap(Bitmap source)
                {
                    this.source = source;
                }

                public void LockBits()
                {
                    try
                    {
                        // Get width and height of bitmap
                        Width = source.Width;
                        Height = source.Height;

                        // get total locked pixels count
                        int PixelCount = Width * Height;

                        // Create rectangle to lock
                        Rectangle rect = new Rectangle(0, 0, Width, Height);

                        // get source bitmap pixel format size
                        Depth = System.Drawing.Bitmap.GetPixelFormatSize(source.PixelFormat);

                        // Check if bpp (Bits Per Pixel) is 8, 24, or 32
                        if (Depth != 8 && Depth != 24 && Depth != 32)
                        {
                            throw new ArgumentException("Only 8, 24 and 32 bpp images are supported.");
                        }

                        // Lock bitmap and return bitmap data
                        bitmapData = source.LockBits(rect, ImageLockMode.ReadWrite,
                                                     source.PixelFormat);

                        //獲得首地址
                        unsafe
                        {
                            Iptr = bitmapData.Scan0;
                            //二維圖像循環
                            
                        }
                    }
                    catch (Exception ex)
                    {
                        throw ex;
                    }
                }

                public void UnlockBits()
                {
                    try
                    {
                        source.UnlockBits(bitmapData);
                    }
                    catch (Exception ex)
                    {
                        throw ex;
                    }
                }

                public Color GetPixel(int x, int y)
                {
                    unsafe
                    {
                        byte* ptr = (byte*)Iptr;
                        ptr = ptr + bitmapData.Stride * y;
                        ptr += Depth * x / 8;
                        Color c = Color.Empty;
                        if (Depth == 32)
                        {
                            int a = ptr[3];
                            int r = ptr[2];
                            int g = ptr[1];
                            int b = ptr[0];
                            c = Color.FromArgb(a, r, g, b);
                        }
                        else if (Depth == 24)
                        {
                            int r = ptr[2];
                            int g = ptr[1];
                            int b = ptr[0];
                            c = Color.FromArgb(r, g, b);
                        }
                        else if (Depth == 8)
                        {
                            int r = ptr[0];
                            c = Color.FromArgb(r, r, r);
                        }
                        return c;
                    }
                }

                public void SetPixel(int x, int y, Color c)
                {
                    unsafe
                    {
                        byte* ptr = (byte*)Iptr;
                        ptr = ptr + bitmapData.Stride * y;
                        ptr += Depth * x / 8;
                        if (Depth == 32)
                        {
                            ptr[3] = c.A;
                            ptr[2] = c.R;
                            ptr[1] = c.G;
                            ptr[0] = c.B;
                        }
                        else if (Depth == 24)
                        {
                            ptr[2] = c.R;
                            ptr[1] = c.G;
                            ptr[0] = c.B;
                        }
                        else if (Depth == 8)
                        {
                            ptr[2] = c.R;
                            ptr[1] = c.G;
                            ptr[0] = c.B;
                        }
                    }
                }
            }
複製代碼

使用方法這裏就不列出來了,跟上面的LockBitmap相似

 

本文實例講述了C#對圖片文件的壓縮、裁剪操做方法,在C#項目開發中很是有實用價值。分享給你們供你們參考。具體以下:

通常在作項目時,對圖片的處理,之前都採用在上傳時,限制其大小的方式,這樣帶來諸多不便。畢竟網站運維人員不必定會對圖片作處理,常常超出大小限制,即便會使用圖片處理軟件的,也因爲我的水平方面緣由,處理效果差強人意。

因而採用C#爲咱們提供的圖像編輯功能,實現一站式上傳,經過程序生成所需大小、尺寸的目標圖片。

具體步驟以下:

先說圖片壓縮:

第一步:須要讀取一個圖片文件,讀取方法:

// <param name="ImageFilePathAndName">圖片文件的全路徑名稱</param> 
public Image ResourceImage =Image.FromFile(ImageFilePathAndName); 

說明:

Image類:引用自System.Drawing,爲源自 Bitmap 和 Metafile 的類提供功能的抽象基類。

主要屬性:Size->獲取此圖像的以像素爲單位的寬度和高度。

PhysicalDimension->獲取此圖像的寬度和高度(若是該圖像是位圖,以像素爲單位返回寬度和高度。若是該圖像是圖元文件,則以0.01 毫米爲單位返回寬度和高度。)。

PixelFormat->獲取此 Image 的像素格式。

Height、Width->獲取此 Image 的高度、寬度(以像素爲單位)。

主要方法:FromFile(String)->從指定的文件建立 Image。

FromStream(Stream)->從指定的數據流建立 Image。

Save(String fileName)->將該 Image 保存到指定的文件或流。

Save(Stream, ImageFormat)->將此圖像以指定的格式保存到指定的流中。

Save(String, ImageFormat)->將此 Image 以指定格式保存到指定文件。

更多屬性和方法說明請點擊。

第二步,生成縮略圖,而且將原圖內容按指定大小繪製到目標圖片

複製代碼
/// <summary> 
/// 生成縮略圖重載方法1,返回縮略圖的Image對象 
/// </summary> 
/// <param name="Width">縮略圖的寬度</param> 
/// <param name="Height">縮略圖的高度</param> 
/// <returns>縮略圖的Image對象</returns> 
public Image GetReducedImage(int Width, int Height) 
{ 
  try
  { 
 //用指定的大小和格式初始化Bitmap類的新實例 
 Bitmap bitmap = new Bitmap(Width, Height, PixelFormat.Format32bppArgb); 
 //從指定的Image對象建立新Graphics對象 
 Graphics graphics = Graphics.FromImage(bitmap); 
 //清除整個繪圖面並以透明背景色填充 
 graphics.Clear(Color.Transparent); 
 //在指定位置而且按指定大小繪製原圖片對象 
 graphics.DrawImage(ResourceImage, new Rectangle(0, 0, Width, Height)); 
 return bitmap; 
  } 
  catch (Exception e) 
  { 
 ErrMessage = e.Message; 
 return null; 
  } 
} 
複製代碼

說明:

一、Bitmap類

引用自System.Drawing,封裝 GDI+ 位圖,此位圖由圖形圖像及其特性的像素數據組成。Bitmap 是用於處理由像素數據定義的圖像的對象。

關於封裝圖像的對象,詳細介紹可參看官方文檔:http://msdn.microsoft.com/zh-cn/library/system.drawing.bitmap.aspx

二、Graphics類

引用自System.Drawing,(處理圖像的對象),封裝一個 GDI+ 繪圖圖面。

關於Graphics類可點此查看官方教程:http://msdn.microsoft.com/zh-cn/library/system.drawing.graphics.aspx

第三步,保存

第二步操做中返回的Image對象,暫時命名爲:iImage:

iImage.Save(pathAndName, System.Drawing.Imaging.ImageFormat.Jpeg); 

以上是壓縮操做,作了下試驗,101k的圖片,通過壓縮後是57k。這個應該和尺寸有關係。

如下是圖片裁剪,其實原理和上面類似,無非也就是對圖片進行重畫操做。

複製代碼
/// <summary> 
/// 截取圖片方法 
/// </summary> 
/// <param name="url">圖片地址</param> 
/// <param name="beginX">開始位置-X</param> 
/// <param name="beginY">開始位置-Y</param> 
/// <param name="getX">截取寬度</param> 
/// <param name="getY">截取長度</param> 
/// <param name="fileName">文件名稱</param> 
/// <param name="savePath">保存路徑</param> 
/// <param name="fileExt">後綴名</param> 
public static string CutImage(string url, int beginX, int beginY, int getX, int getY, string fileName, string savePath, string fileExt) 
{ 
  if ((beginX < getX) && (beginY < getY)) 
  { 
 Bitmap bitmap = new Bitmap(url);//原圖 
if (((beginX + getX) <= bitmap.Width) && ((beginY + getY) <= bitmap.Height)) 
 { 
   Bitmap destBitmap = new Bitmap(getX, getY);//目標圖 
   Rectangle destRect = new Rectangle(0, 0, getX, getY);//矩形容器 
   Rectangle srcRect = new Rectangle(beginX, beginY, getX, getY); 
 
   Graphics.FromImage(destBitmap); 
            Graphics.DrawImage(bitmap, destRect, srcRect, GraphicsUnit.Pixel); 
     
   ImageFormat format = ImageFormat.Png; 
   switch (fileExt.ToLower()) 
   { 
 case "png": 
   format = ImageFormat.Png; 
   break; 
 case "bmp": 
   format = ImageFormat.Bmp; 
   break; 
 case "gif": 
   format = ImageFormat.Gif; 
   break; 
   } 
   destBitmap.Save(savePath + "//" + fileName , format); 
   return savePath + "\\" + "*" + fileName.Split('.')[0] + "." + fileExt; 
 } 
 else
 { 
   return "截取範圍超出圖片範圍"; 
 } 
  } 
  else
  { 
 return "請確認(beginX < getX)&&(beginY < getY)"; 
  } 
} 
複製代碼

說明:

Rectangle類:矩形,詳情可參考官方文檔:http://msdn.microsoft.com/zh-cn/library/system.windows.shapes.rectangle(v=vs.85).aspx

相關文章
相關標籤/搜索