C#語言新特性(6.0-8.0)

来源:https://www.cnblogs.com/xclw/archive/2020/04/16/12653428.html
-Advertisement-
Play Games

只讀的自動屬性 通過聲明只有get訪問器的自動屬性,實現該屬性只讀 public string FirstName { get; } public string LastName { get; } 自動只讀屬性在能在構造函數中賦值,任何其他地方的賦值都會報編譯錯誤。 自動屬性初始化器 在聲明自動屬性 ...


只讀的自動屬性

通過聲明只有get訪問器的自動屬性,實現該屬性只讀

public string FirstName { get; }
public string LastName { get;  }

自動只讀屬性在能在構造函數中賦值,任何其他地方的賦值都會報編譯錯誤。

自動屬性初始化器

在聲明自動屬性時,還可以給它指定一個初始值。初始值作為整個聲明的一部分。

public ICollection<double> Grades { get; } = new List<double>();

字元串插入

允許你在字元串中嵌入表達式。字元串以$開頭,把要嵌入的表達式在相應的位置用{和}包起來。

public string FullName => $"{FirstName} {LastName}";

你還可以對錶達式進行格式化

public string GetGradePointPercentage() =>
    $"Name: {LastName}, {FirstName}. G.P.A: {Grades.Average():F2}";

 異常過濾器

public static async Task<string> MakeRequest()
{
    WebRequestHandler webRequestHandler = new WebRequestHandler();
    webRequestHandler.AllowAutoRedirect = false;
    using (HttpClient client = new HttpClient(webRequestHandler))
    {
        var stringTask = client.GetStringAsync("https://docs.microsoft.com/en-us/dotnet/about/");
        try
        {
            var responseText = await stringTask;
            return responseText;
        }
        catch (System.Net.Http.HttpRequestException e) when (e.Message.Contains("301"))
        {
            return "Site Moved";
        }
    }
}

nameof表達式

獲取變數、屬性或者成員欄位的名稱

if (IsNullOrWhiteSpace(lastName))
    throw new ArgumentException(message: "Cannot be blank", paramName: nameof(lastName));

await應用於catch和finally代碼塊

 這個就不多說了,很簡單,看代碼吧

public static async Task<string> MakeRequestAndLogFailures()
{ 
    await logMethodEntrance();
    var client = new System.Net.Http.HttpClient();
    var streamTask = client.GetStringAsync("https://localHost:10000");
    try {
        var responseText = await streamTask;
        return responseText;
    } catch (System.Net.Http.HttpRequestException e) when (e.Message.Contains("301"))
    {
        await logError("Recovered from redirect", e);
        return "Site Moved";
    }
    finally
    {
        await logMethodExit();
        client.Dispose();
    }
}

通過索引器初始化集合

索引初始化器使得對集合元素的初始化與通過索引訪問保持一致。之前對Dictionary的初始化使用大括弧的方式,如下:

private Dictionary<int, string> messages = new Dictionary<int, string>
{
    { 404, "Page not Found"},
    { 302, "Page moved, but left a forwarding address."},
    { 500, "The web server can't come out to play today."}
};

現在你可以通過類似索引訪問的方式進行初始化,上面的代碼可以改為:

private Dictionary<int, string> webErrors = new Dictionary<int, string>
{
    [404] = "Page not Found",
    [302] = "Page moved, but left a forwarding address.",
    [500] = "The web server can't come out to play today."
};

out變數聲明

前要調用一個還有out參數的方法前,你需要先聲明一個變數並賦一個初始值,然後才能調用這個方法

int result=0;
if (int.TryParse(input, out result))
    Console.WriteLine(result);
else
    Console.WriteLine("Could not parse input");

現在可以在調用方法的同時聲明out變數

if (int.TryParse(input, out int result))
    Console.WriteLine(result);
else
    Console.WriteLine("Could not parse input");

同時這種方式還支持隱式類型,你可以用var代理實際的參數類型

if (int.TryParse(input, out var answer))
    Console.WriteLine(answer);
else
    Console.WriteLine("Could not parse input");

加強型元組(Tuple)

在7.0之前,要使用元組必須通過new Tuple<T1, T2....>()這種方式,並且元組中的各元素只能通過屬性名Item1, Item2...的方式訪問,費力且可讀性不強。

現在你可以通過如下方式聲明元組,給元組賦值,且給元組中的每個屬性指定一個名稱

(string Alpha, string Beta) namedLetters = ("a", "b");
Console.WriteLine($"{namedLetters.Alpha}, {namedLetters.Beta}");

元組namedLetters包含兩個欄位,Alpha和Beta。欄位名只在編譯時有效,在運行時又會變成Item1, Item2...的形式。所以在反射時不要用這些名字。

你還可以在賦值時,在右側指定欄位的名字,查看下麵的代碼

var alphabetStart = (Alpha: "a", Beta: "b");
Console.WriteLine($"{alphabetStart.Alpha}, {alphabetStart.Beta}");

此外,編譯器還可以從變數中推斷出欄位的名稱,例如下麵的代碼

int count = 5;
string label = "Colors used in the map";
var pair = (count: count, label: label);
//上面一行,可以換個寫法,欄位名自動從變數名中推斷出來了
var pair = (count, label);

你還可以對從方法返回的元組進行拆包操作,為元組中的每個成員聲明獨立的變數,以提取其中的成員。這個操作稱為解構。查看如下代碼

(int max, int min) = Range(numbers);
Console.WriteLine(max);
Console.WriteLine(min);

你可以為任意.NET類型提供類似的解構操作。為這個類提供一個Deconstruct方法,此方法需要一組out參數,每個要提取的屬性對應一個out參數。

    public class User
    {
        public User(string fullName)
        {
            var arr = fullName.Split(' ');
            (FirstName, LastName) = (arr[0], arr[1]);
        }

        public string FirstName { get; }
        public string LastName { get; }

        public void Deconstruct(out string firstName, out string lastName) =>
            (firstName, lastName) = (this.FirstName, this.LastName);
    }

通過把User賦值給一個元組,就可以提取各個欄位了

            var user = new User("Rock Wang");
            (string first, string last) = user;
            Console.WriteLine($"First Name is: {first}, Last Name is: {last}");

捨棄物

經常會遇到這樣的情況:在解構元組或者調用有out參數的方法時,有些變數的值你根本不關心,或者在後續的代碼也不打算用到它,但你還是必須定義一個變數來接收它的值。C#引入了捨棄物的概念來處理這種情況。

捨棄物是一個名稱為_(下劃線)的只讀變數,你可以把所有想捨棄的值賦值給同一個捨棄物變數,捨棄物變數造價於一個未賦值的變數。捨棄物變數只能在給它賦值的語句中使用,在其它地方不能使用。

捨棄物可以使用在以下場景中:

  • 對元組或者用戶定義的類型進行解構操作時
using System;
using System.Collections.Generic;

public class Example
{
    public static void Main()
    {
        var (_, _, _, pop1, _, pop2) = QueryCityDataForYears("New York City", 1960, 2010);

        Console.WriteLine($"Population change, 1960 to 2010: {pop2 - pop1:N0}");
    }
   
    private static (string, double, int, int, int, int) QueryCityDataForYears(string name, int year1, int year2)
    {
        int population1 = 0, population2 = 0;
        double area = 0;
      
        if (name == "New York City")
        {
            area = 468.48; 
            if (year1 == 1960)
            {
                population1 = 7781984;
            }
            if (year2 == 2010)
            {
                population2 = 8175133;
            }
            return (name, area, year1, population1, year2, population2);
        }

        return ("", 0, 0, 0, 0, 0);
    }
}
// The example displays the following output:
//      Population change, 1960 to 2010: 393,149
  • 調用帶有out參數的方法時
using System;

public class Example
{
   public static void Main()
   {
      string[] dateStrings = {"05/01/2018 14:57:32.8", "2018-05-01 14:57:32.8",
                              "2018-05-01T14:57:32.8375298-04:00", "5/01/2018",
                              "5/01/2018 14:57:32.80 -07:00", 
                              "1 May 2018 2:57:32.8 PM", "16-05-2018 1:00:32 PM", 
                              "Fri, 15 May 2018 20:10:57 GMT" };
      foreach (string dateString in dateStrings)
      {
         if (DateTime.TryParse(dateString, out _)) 
            Console.WriteLine($"'{dateString}': valid");
         else
            Console.WriteLine($"'{dateString}': invalid");
      }
   }
}
// The example displays output like the following:
//       '05/01/2018 14:57:32.8': valid
//       '2018-05-01 14:57:32.8': valid
//       '2018-05-01T14:57:32.8375298-04:00': valid
//       '5/01/2018': valid
//       '5/01/2018 14:57:32.80 -07:00': valid
//       '1 May 2018 2:57:32.8 PM': valid
//       '16-05-2018 1:00:32 PM': invalid
//       'Fri, 15 May 2018 20:10:57 GMT': invalid
  • 在進行帶有is和switch語句的模式匹配時(模式匹配下麵會講到)
using System;
using System.Globalization;

public class Example
{
   public static void Main()
   {
      object[] objects = { CultureInfo.CurrentCulture, 
                           CultureInfo.CurrentCulture.DateTimeFormat, 
                           CultureInfo.CurrentCulture.NumberFormat,
                           new ArgumentException(), null };
      foreach (var obj in objects)
         ProvidesFormatInfo(obj);
   }

   private static void ProvidesFormatInfo(object obj)         
   {
      switch (obj)
      {
         case IFormatProvider fmt:
            Console.WriteLine($"{fmt} object");
            break;
         case null:
            Console.Write("A null object reference: ");
            Console.WriteLine("Its use could result in a NullReferenceException");
            break;
         case object _:
            Console.WriteLine("Some object type without format information");
            break;
      }
   }
}
// The example displays the following output:
//    en-US object
//    System.Globalization.DateTimeFormatInfo object
//    System.Globalization.NumberFormatInfo object
//    Some object type without format information
//    A null object reference: Its use could result in a NullReferenceException
  • 在任何你想忽略一個變數的時,它可以作為一個標識符使用
using System;
using System.Threading.Tasks;

public class Example
{
   public static async Task Main(string[] args)
   {
      await ExecuteAsyncMethods();
   }

   private static async Task ExecuteAsyncMethods()
   {    
      Console.WriteLine("About to launch a task...");
      _ = Task.Run(() => { var iterations = 0;  
                           for (int ctr = 0; ctr < int.MaxValue; ctr++)
                              iterations++;
                           Console.WriteLine("Completed looping operation...");
                           throw new InvalidOperationException();
                         });
      await Task.Delay(5000);                        
      Console.WriteLine("Exiting after 5 second delay");
   }
}
// The example displays output like the following:
//       About to launch a task...
//       Completed looping operation...
//       Exiting after 5 second delay

ref局部化和返回值

此特性允許你對一個在別的地方定義的變數進行引用,並可以把它以引用的形式返回給調用者。下麵的例子用來操作一個矩陣,找到一個具有某一特征的位置上的元素,並返回這個元素的引用。

public static ref int Find(int[,] matrix, Func<int, bool> predicate)
{
    for (int i = 0; i < matrix.GetLength(0); i++)
        for (int j = 0; j < matrix.GetLength(1); j++)
            if (predicate(matrix[i, j]))
                return ref matrix[i, j];
    throw new InvalidOperationException("Not found");
}

你可以把返回值聲明成ref並修改保存在原矩陣中的值。

ref var item = ref MatrixSearch.Find(matrix, (val) => val == 42);
Console.WriteLine(item);
item = 24;
Console.WriteLine(matrix[4, 2]);

為了防止誤用,C#要求在使用ref局部化和返回值時,需要遵守以下規則:

  • 定義方法時,必須在方法簽名和所有的return語句上都要加上ref關鍵字
  • ref返回值可以賦值給一個值變數,可以賦值給引用變數
  • 不能把一個普通方法的返回值賦值一個ref的局部變數,像 ref int i = sequence.Count() 這樣的語句是不允許的。
  • 要返回的ref變數,作用域不能小於方法本身。如果是方法的局部變數,方法執行完畢後,其作用域也消失了,這樣的變數是不能被ref返回的
  • 不能在非同步(async)方法中使用

幾個提升性能的代碼改進

當以引用的方式操作一些值類型時,可用如下幾種方式,起到減少記憶體分配,提升性能的目的。

  1. 給參數加上 in 修飾符。in 是對現有的 ref 和 out的補充。它指明該參數以引用方式傳遞,但在方法內它的值不會被修改。在給方法傳遞值類型參數量,如果沒有指定out, ref和in中的任意一種修飾符,那該值在記憶體中會被覆制一份。這三種修飾符指明參數值以引用方式傳遞,從而避免被覆制。當傳遞的參數類型是比較大的結構(通過批大於IntPtr.Size)時,對性能的提升比較明顯;對於一些小的值類型,其作用並不明顯,甚至會降低性能,比如sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, bool, enum等。這些修飾行有各自的作用,分別如下:
    • out: 在方法內必須修改參數的值
    • ref: 在方法內可能會修改參數的值
    • in: 在方法內不能修改參數的值
  2. 對ref返回值(參見特性ref局部化和返回值),如果你不想調用方修改返回的值,可以在返回時加上ref readonly,同時調用者也要用ref readonly變數來接收返回值,所以之前的代碼可以修改如下:
    public static ref readonly int Find(int[,] matrix, Func<int, bool> predicate)
    {
        for (int i = 0; i < matrix.GetLength(0); i++)
            for (int j = 0; j < matrix.GetLength(1); j++)
                if (predicate(matrix[i, j]))
                    return ref matrix[i, j];
        throw new InvalidOperationException("Not found");
    }
    ref readonly var item = ref MatrixSearch.Find(matrix, (val) => val == 42);
    Console.WriteLine(item);
    item = 24;
    Console.WriteLine(matrix[4, 2]);
  3. 聲明結構體時加上readonly修飾符,用來指明該struct是不可修改的,並且應當以in參數的形式傳給方法

非顯式命名參數

命名參數是指給方法傳參時可以以“參數名:參數值”的形式傳參而不用管該參數在方法簽名中的位置。

    static void PrintOrderDetails(string sellerName, int orderNum, string productName)
    {
        if (string.IsNullOrWhiteSpace(sellerName))
        {
            throw new ArgumentException(message: "Seller name cannot be null or empty.", paramName: nameof(sellerName));
        }

        Console.WriteLine($"Seller: {sellerName}, Order #: {orderNum}, Product: {productName}");
    }

 

PrintOrderDetails(orderNum: 31, productName: "Red Mug", sellerName: "Gift Shop");

PrintOrderDetails(productName: "Red Mug", sellerName: "Gift Shop", orderNum: 31);

如上面的調用,是對同一方法的調用,而非重載方法,可見參數位置可以不按方法簽名中的位置。如果某一參數出現的位置同它在方法簽名中的位置相同,則可以省略參數名,只傳參數值。如下所示:

PrintOrderDetails(sellerName: "Gift Shop", 31, productName: "Red Mug");

上面的例子中orderNum在正確的位置上,只傳參數值就可以了,不用指定參數名;但如果參數沒有出現豐正確的位置上,就必須指定參數名,下麵的語句編譯器會拋出異常

// This generates CS1738: Named argument specifications must appear after all fixed arguments have been specified.
PrintOrderDetails(productName: "Red Mug", 31, "Gift Shop");

 

表達式體成員

有些函數或者屬性只有一條語句,它可能只是一個表達式,這時可以用表達式體成員來代替

// 在構造器中使用
public ExpressionMembersExample(string label) => this.Label = label;

// 在終結器中使用
~ExpressionMembersExample() => Console.Error.WriteLine("Finalized!");

private string label;

// 在get, set存取器中使用
public string Label
{
    get => label;
    set => this.label = value ?? "Default label";
}

//在方法中使用
public override string ToString() => $"{LastName}, {FirstName}";

//在只讀屬性中使用
public string FullName => $"{FirstName} {LastName}";

throw表達式

在7.0之前,throw只能作為語句使用。這使得在一些場景下不支持拋出異常,這些場景包括:

  • 條件操作符。如下麵的例子,如果傳入的參數是一個空的string數組,則會拋出異常,如果在7.0之前,你需要用到 if / else語句,現在不需要了
private static void DisplayFirstNumber(string[] args)
{
   string arg = args.Length >= 1 ? args[0] : 
                              throw new ArgumentException("You must supply an argument");
   if (Int64.TryParse(arg, out var number))
      Console.WriteLine($"You entered {number:F0}");
   else
      Console.WriteLine($"{arg} is not a number.");                            
}
  • 在空接合操作符中。在下麵的例子中,throw表達式跟空接合操作符一起使用。在給Name屬性賦值時,如果傳入的value是null, 則拋出異常
public string Name
{
    get => name;
    set => name = value ?? 
        throw new ArgumentNullException(paramName: nameof(value), message: "Name cannot be null");
}
  • 在lambda表達式或者具有表達式體的方法中
DateTime ToDateTime(IFormatProvider provider) => 
         throw new InvalidCastException("Conversion to a DateTime is not supported.");

數值寫法的改進

數值常量常常容易寫錯或者讀錯。c#引入了更易讀的寫法

public const int Sixteen =   0b0001_0000;
public const int ThirtyTwo = 0b0010_0000;
public const int SixtyFour = 0b0100_0000;
public const int OneHundredTwentyEight = 0b1000_0000;

開頭的 0b 表示這是一個二進位數,_(下劃線) 表示數字分隔符。分隔符可以出現在這個常量的任意位置,只要能幫助你閱讀就行。比如在寫十進位數時,可以寫成下麵的形式

public const long BillionsAndBillions = 100_000_000_000;

分隔符還可以用於 decimal, float, double類型

public const double AvogadroConstant = 6.022_140_857_747_474e23;
public const decimal GoldenRatio = 1.618_033_988_749_894_848_204_586_834_365_638_117_720_309_179M;

從7.2開始,二進位和十六進位的數組還可以 _ 開頭

int binaryValue = 0b_0101_0101;
int hexValue = 0x_ffee_eeff;

private protected訪問修飾符

private protected指明一個成員只能被包含類(相對內部類而言)或者在同一程式集下的派生類訪問

註:protected internal指明一個成員只能被派生類或者在同一程式集內的其他類訪問

條件的ref表達式

現在條件表達式可以返回一個ref的結果了,如下:

ref var r = ref (arr != null ? ref arr[0] : ref otherArr[0]);

 

 

非同步Main方法

async main 方法使你能夠在Main方法中使用 await。之前你可能需要這麼寫:

static int Main()
{
    return DoAsyncWork().GetAwaiter().GetResult();
}

現在你可以這麼寫:

static async Task<int> Main()
{
    // This could also be replaced with the body
    // DoAsyncWork, including its await expressions:
    return await DoAsyncWork();
}

如果你的程式不需要返回任何退出碼,你可以讓Main方法返回一個Task:

static async Task Main()
{
    await SomeAsyncMethod();
}

default字面的表達式

default字面的表達式是對defalut值表達式的改進,用於給變數賦一個預設值。之前你是這麼寫的:

Func<string, bool> whereClause = default(Func<string, bool>);

現在你可以省略右邊的類型

Func<string, bool> whereClause = default;

 

using static

using static語句允許你把一個類中的靜態方法導出進來,在當前文件中可以直接使用它的靜態方法,而不用帶上類名

using static System.Math
//舊寫法
System.Math.Abs(1, 2, 3);

//新寫法
Abs(1, 2, 3);

 空條件操作符(null-conditional operator)

空條件操作符使判空更加容易和流暢。把成員訪問操作符 . 換成 ?.

var first = person?.FirstName;

在上述代碼中,如果person為null,則把null賦值給first,並不會拋出NullReferenceException;否則,把person.FirstName賦值給first。你還可以把空條件操作符應用於只讀的自動屬性

通過聲明只有get訪問器的自動屬性,實現該屬性只讀

 

public string FirstName { get; }

public string LastName { get; }

 

自動只讀屬性在能在構造函數中賦值,任何其他地方的賦值都會報編譯錯誤。

 

自動屬性初始化器

在聲明自動屬性時,還可以給它指定一個初始值。初始值作為整個聲明的一部分。

public ICollection<double> Grades { get; } = new List<double>();

局部函數(Local functions)

有些方法只在一個地方被調用,這想方法通常很小且功能單一,沒有很複雜的邏輯。局部函數允許你在一個方法內部聲明另一個方法。局部函數使得別人一眼就能看出這個方法只在聲明它的方法內使用到。代碼如下:

int M()
{
    int y;
    AddOne();
    return y;

    void AddOne() => y += 1;
}

上面的代碼中, AddOne就是一個局部函數,它的作用是給y加1。有時候你可能希望這些局部函數更“獨立”一些,不希望它們直接使用上下文中的變數,這時你可以把局部函數聲明成靜態方法,如果你在靜態方法中使用了上下文中的變數,編譯器會報錯CS8421。如下代碼所示:

int M()
{
    int y;
    AddOne();
    return y;

    static void AddOne() => y += 1;
}

這時你的代碼要做相應的修改

int M()
{
    int y;
    y=AddOne(y);
    return y;

    static intAddOne(int toAdd) =>{ toAdd += 1; return toAdd;}
}

序列的下標和範圍

在通過下標取序列的元素時,如果在下麵前加上 ^ 表示從末尾開始計數。操作符 .. 兩邊的數表示開始下標和結束下標,假設有如下數組

var words = new string[]
{
                // index from start    index from end
    "The",      // 0                   ^9
    "quick",    // 1                   ^8
    "brown",    // 2                   ^7
    "fox",      // 3                   ^6
    "jumped",   // 4                   ^5
    "over",     // 5                   ^4
    "the",      // 6                   ^3
    "lazy",     // 7                   ^2
    "dog"       // 8                   ^1
};              // 9 (or words.Length) ^0

你可以通過 ^1下標來取最後一個元素(註意:^0相當於words.Length,會拋出異常)

Console.WriteLine($"The last word is {words[^1]}");
// writes "dog"

下麵的代碼會取出一個包含"quick", "brown"和"fox"的子集,分別對應words[1], words[2], words[3]這3個元素,words[4]不包括

var quickBrownFox = words[1..4];

下麵的代碼會取出"lazy"和"dog"的子集體,分別對應words[^2]和words[^1]。wrods[^0]不包括。

var lazyDog = words[^2..^0];

空聯合賦值

先回憶一下空聯合操作符 ??

它表示如果操作符左邊不為null,則返回它。否則,返回操作符右邊的計算結果

int? a = null;
int b = a ?? -1;
Console.WriteLine(b);  // output: -1

空聯合賦值:當??左邊為null時,把右邊的計算結果賦值給左邊

List<int> numbers = null;
int? a = null;

(numbers ??= new List<int>()).Add(5);
Console.WriteLine(string.Join(" ", numbers));  // output: 5

numbers.Add(a ??= 0);
Console.WriteLine(string.Join(" ", numbers));  // output: 5 0
Console.WriteLine(a);  // output: 0

內插值替換的string的增強

$@現在等價於@$

var text1 = $@"{a}_{b}_{c}";
var text2 = @$"{a}_{b}_{c}";

只讀成員(Readonly Members)

可以把 readonly 修飾符應用於struct的成員上,這表明該成員不會修改狀態。相對於在 struct 上應用readonly顯示更加精細化。

考慮正面這個可變結構體:

public struct Point
{
    public double X { get; set; }
    public double Y { get; set; }
    public double Distance => Math.Sqrt(X * X + Y * Y);

    public override string ToString() =>
        $	   

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 0. 前言 上一篇文章介紹了字元串自身的一些方法,就是對象方法。在字元串體系中,還有一些是 類提供的靜態方法。這兩部分構成了字元串體系,當然還有一些三方庫為字元串提供了擴展方法。 這裡簡單的介紹一下 類的靜態方法。 1. 玩轉創建字元串 1.1 Create一個字元串 通過調用 方法可以生成一個字元 ...
  • 說到lock鎖,我相信在座的各位沒有不會用的,而且還知道怎麼用不會出錯,但讓他們聊一聊為什麼可以鎖住,都說人以群分,大概就有了下麵低中高水平的三類人吧。 第一類人 將lock對象定義成static,這樣就能讓多個線程看到同一個對象,以此實現線程間互斥和保證同步,如果再深問為什麼?就怕遮遮掩掩的說好像 ...
  • 分散式緩存由一個服務端實現管理和控制,有多個客戶端節點存儲數據,可以進一步提高數據的讀取速率。那麼我們要讀取某個數據的時候,應該選擇哪個節點呢?如果挨個節點找,那效率就太低了。因此需要根據一致性哈希演算法確定數據的存儲和讀取節點。以數據D,節點總個數N為基礎,通過一致性哈希演算法計算出數據D對應的哈希值 ...
  • class TestClass { public static void test(params int[] array) { string s;foreach (int i in array) { Console.WrriteLine(i); } } } public static void Ma ...
  • 一、邏輯運算符說明 邏輯運算符,顧名思義就是邏輯判斷,即結果為真或假 二、 &和&&、 |和||之間的區別 &和&&: 相同點 : 兩者都是表達當左右兩邊操作數都為真時,才為真. 不同點 : &運算符需要判斷兩邊的操作數,而&&運算符則是根據第一個操作數的真假進而判斷第二個操作數,也就是說當第一個操 ...
  • .NET 下基於動態代理的 AOP 框架實現揭秘 Intro 之前基於 Roslyn 實現了一個簡單的條件解析引擎,想瞭解的可以看這篇文章 執行過程中會根據條件的不同會在運行時創建一個類,每一次創建都會生成一個新的程式集,我覺得這樣實現的話可能會導致載入的程式集越來越多,雖然目前我們的使用場景下不會 ...
  • 1、需求 前段時間有個需求,要求把網頁生成pdf,找了各種插件,才決定使用這個TuesPechkin,這個是後臺採用C#代碼進行生成 2、做法 我要做的是一個比較簡單的頁面,採用MVC綁定,數據動態載入,頁面上給個按鈕,點擊後請求後臺介面,便可以生成pdf文件了 3、實現方式 引入兩個相關DLL,T ...
  • private void timer1_Tick(object sender, EventArgs e) { label1.Left -= 2; if (label1.Right < 0) { label1.Left = this.Width; } } 在窗體的定時器上編寫上面的代碼就可以看到效果了 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...