狠狠色丁香婷婷综合尤物/久久精品综合一区二区三区/中国有色金属学报/国产日韩欧美在线观看 - 国产一区二区三区四区五区tv

LOGO OA教程 ERP教程 模切知識交流 PMS教程 CRM教程 開發(fā)文檔 其他文檔  
 
網(wǎng)站管理員

C# 揭秘你可能不知道的高效編程技巧

admin
2024年9月19日 1:33 本文熱度 772

前言

本文來告訴大家在C#很少有人會發(fā)現(xiàn)的科技。即使是工作了好多年的老司機(jī)也不一定會知道這些科技,如果覺得我是在騙你,那么請看看本文的內(nèi)容。

原本最初 C# 的設(shè)計是簡單和高效開發(fā)的,在經(jīng)過了這么多年眾多公司和開發(fā)者的努力下,整個 C# 里面包含了大量有趣的功能。其中一部分功能是針對于某些特殊需求設(shè)計的,例如高性能或高并發(fā)或無內(nèi)存回收等。在經(jīng)過了 10 多年的迭代,很少人能完全了解整個 C# 語言和框架級做了哪些有趣的功能。

在網(wǎng)上找了很多大神的博客,然后和很多大神聊天,知道了一些科技,于是就在本文和大家分享一下。如果大家有了解本博客里面沒有收藏的科技,還請告訴我。

現(xiàn)在整個 C# 從編譯器到運(yùn)行時都是開源的,所有權(quán)在 dotnet 基金會上,全部開源的項目都基于最友好的 MIT 協(xié)議和 Apache 2 開源協(xié)議,文檔協(xié)議遵循CC-BY協(xié)議。這將允許任何人任何組織和企業(yè)任意處置,包括使用,復(fù)制,修改,合并,發(fā)表,分發(fā),再授權(quán),或者銷售。唯一的限制是,軟件中必須包含上述版 權(quán)和許可提示,后者協(xié)議將會除了為用戶提供版權(quán)許可之外,還有專利許可,并且授權(quán)是免費(fèi),無排他性的(任何個人和企業(yè)都能獲得授權(quán))并且永久不可撤銷,用戶使用.NET 和 C# 完全不用擔(dān)心收費(fèi)問題和版權(quán)問題,以及后續(xù)無法維護(hù)問題。而 dotnet 基金會是一個開放的平臺,我也是 dotnet 基金會的成員之一。微軟在 2020 的時候依然是 dotnet 基金會最大的支持組織

現(xiàn)在最火的 dotnet 倉庫是 dotnet csharplang 倉庫,當(dāng)前的 C# 語言特性由整個社區(qū)決定,這是一個官方開放用來討論 C# 語言未來的倉庫,天天都有大佬們在討論語言的特性,歡迎大家加入。

接下來讓我告訴大家一些很少有人會發(fā)現(xiàn)的科技

1、無限級判斷空

在 C# 6.0 可以使用??判斷空,那么就可以使用下面代碼

var v1 = "123";
string v2 = null;
string v3 = null;

var v = v1 ?? v2 ?? v3;

實(shí)際上可以無限的使用??判斷前面一個函數(shù)為空,那么問題來了,下面的代碼輸出的是多少?

var n = 2 + foo?.N ?? 1;

上面代碼的 foo 就是空的,那么 n 是多少?是 1 還是 2 還是 3 還是空?
想要了解這道題的推導(dǎo)過程請看C# 高級面試題 里面寫了很多老司機(jī)都不一定能解出
2、使用 using 關(guān)鍵詞省略長的定義
例如有下面這個代碼,在這個代碼里面使用了很多的 List 嵌套,如下面代碼所示里面有很多定義的代碼

var foo = new System.Collections.Generic.Dictionary<System.Collections.Generic.List<System.Collections.Generic.List<string>>, string>();

可以看到上面代碼中,有大量的代碼都是用來作為類型的定義,假設(shè)這個值作為某個方法的參數(shù),那才是可怕
一個簡單的方法是使用 using 關(guān)鍵詞,如在文件的開頭添加如下代碼

using HvcnrclHnlfk = System.Collections.Generic.Dictionary<System.Collections.Generic.List<System.Collections.Generic.List<string>>,string>;

在添加了上面代碼之后,在這個文件里的所有用到如上面很長的定義的代碼都可以使用 using 后面的值可以代替,如本文上面使用了 HvcnrclHnlfk 這個詞,來看看替換之后的代碼長度

var foo = new HvcnrclHnlfk();

3、辣么大

實(shí)際上寫到這里我有些不好意思,好像剛剛說的都是大家都知道的,那么我就要開始寫大家很少知道的科技
等等,什么是 辣么大 大哇?其實(shí)這是 lambda 表達(dá)式的翻譯
請看看下面這段有趣的代碼

Func<string,string, EventHandler> foo = (x, y) => (s, e) =>
{
var button = (Button) s;
button.Left = x;
button.Top = y;
};

Button1.Click += foo(0, -1);

上面的代碼通過一個 lambda 表達(dá)式返回一個另一個 lambda 表達(dá)式,或者說用一個委托返回另一個委托。這是一個特別有趣的寫法,通過函數(shù)返回函數(shù)的思想可以用來寫出一些有趣的邏輯,特別是在多層嵌套的時候
當(dāng)然使用委托可是會出現(xiàn)另一個問題的,請問下面的代碼實(shí)際調(diào)用的是哪個委托,下面代碼的 a 和 b 和 c 都是 Action 委托,同時都不是空的

((a + b + c) - (a + c))();

在數(shù)學(xué)上,其實(shí)函數(shù)也可以視為變量,很有科技范的 C# 當(dāng)然也支持如此的功能,將函數(shù)包裝為委托的時候,可以讓委托本身支持加減法哦,只是這個加減法的規(guī)則有些詭異。不信,請猜猜上面代碼執(zhí)行了什么函數(shù)
4、沖突的類型
在遇到某些類型,特別是放在 NuGet 上的多個不同的庫里面的類型,這些類型有相同的類名,如 Data 或 Control 等很通用的命名的時候,在代碼中如果需要同時使用這兩個類,就需要補(bǔ)全整個命名空間,如下面代碼

var webControl = new System.Web.UI.WebControls.Control();
var formControl = new System.Windows.Forms.Control();

如果經(jīng)常使用這兩個控件,那么就需要寫很多補(bǔ)全命名空間的代碼,代碼很多。好在微軟的大佬們給出了一個坑方法,使用這個方法可以不寫命名空間,或者說只需要在文件開始 using 一次,請看代碼

using web = System.Web.UI.WebControls;
using win = System.Windows.Forms;

web::Control webControl = new web::Control();
win::Control formControl = new win::Control();

參見:https://stackoverflow.com/a/9099/6116637
5、extern alias
如果使用了兩個不同的程序集放在兩個不同的 dll 文件里面,這兩個程序集都有相同命名空間和類型,那么如何使用指定的庫
如下面代碼所示,在兩個 dll 里面都定義了 F.Foo 類型

//a.dll
namespace F
{
public class Foo
{

}
}
//b.dll
namespace F
{
public class Foo
{

}
}

這時就可以使用 extern alias 關(guān)鍵詞
6、字符串
大家看到了 C# 6.0 的$,是不是可以和@一起?

var str = "kktpqfThiq";
string foo = $@"換行
{str}"
;

注意兩個的順序,反過來直接告訴你代碼不能這樣寫
此知識點(diǎn)不再適用,因為在 C# 8.0 的時候,可以按照任意的順序使用 $@ 標(biāo)記。
7、特殊關(guān)鍵字
實(shí)際上有下面幾個關(guān)鍵字是沒有詳細(xì)的文檔,可能只有微軟的編譯器才知道

__makeref

__reftype

__refvalue

__arglist

不過在 C# 7.2 可以使用其他的關(guān)鍵字做到一些功能,詳細(xì)請看我的 C# 7.0 博客
8、使用 Unions (C++ 一樣的)
如果看到 C++ 可以使用內(nèi)聯(lián),不要說 C# 沒有這個功能,實(shí)際上也可以使用 FieldOffset 特性實(shí)現(xiàn)和 C++ 一樣的內(nèi)聯(lián)的功能 ,請看下面代碼

[StructLayout(LayoutKind.Explicit)]
public class A
{
[FieldOffset(0)]
public byte One;

[FieldOffset(1)]
public byte Two;

[FieldOffset(2)]
public byte Three;

[FieldOffset(3)]
public byte Four;

[FieldOffset(0)]
public int Int32;
}

如下面代碼就定義了int變量,修改這個變量就是修改其他的三個變量

static void Main(string[] args)
{
A a = new A { Int32 = int.MaxValue };

Console.WriteLine(a.Int32);
Console.WriteLine("{0:X} {1:X} {2:X} {3:X}", a.One, a.Two, a.Three, a.Four);

a.Four = 0;
a.Three = 0;
Console.WriteLine(a.Int32);
}

運(yùn)行代碼可以看到輸出如下

2147483647
FF FF FF 7F
65535

可以看到修改其中某個值都會相互影響,這幾個值共用了相同的一個內(nèi)存空間
9、接口默認(rèn)方法
實(shí)際上可以給接口使用默認(rèn)方法,使用的方式如下

public static void Foo(this IF1 foo)
{
//實(shí)際上大家也看到是如何定義
}

當(dāng)然了,在 C# 8.0 還有更直接的方法,詳細(xì)請看 在 C# 中使用默認(rèn)接口方法安全地更新接口
10、stackalloc
很多人都不知道這個科技,這是不安全代碼,從棧申請空間

int* block = stackalloc int[100];

使用的時候需要小心你的棧也許會炸掉
參見:stackalloc
11、指定編譯
這個是一個有趣的特性實(shí)現(xiàn)的功能,是一個編譯器技術(shù),寫給編譯器看的特性。使用 Conditional 特性可以讓代碼在指定條件不使用,如下面的代碼,規(guī)定了只有在 DEBUG 宏定義的時候才讓 F2 方法生效。因此在 Release 下就不會使用 F2 方法了

public sealed clas Foo
{
public Foo F1()
{
Console.WriteLine("進(jìn)入F1");
return this;
}

[Conditional("DEBUG")]
public void F2()
{
Console.WriteLine("F2");
}
}

簡單讓代碼跑一下

static void Main(string[] args)
{
var foo = new Foo();
foo.F1();
foo.F2();
}

結(jié)果是什么,大家也知道,在 Debug 和 Release 輸出是不相同。但是這么簡單的怎么會在這里說呢,請大家看看這個代碼輸出什么

static void Main(string[] args)
{
var foo = new Foo();
foo.F1().F2();
}

實(shí)際上在 Release 下什么都不會輸出,此時的 F1 不會被執(zhí)行
12、true 判斷
下面寫個見鬼的代碼

var foo = new Foo(10);

if (foo)
{
Console.WriteLine("我的類沒有繼承 bool ,居然可以這樣寫");
}

沒錯 Foo 沒有繼承 bool 居然可以這樣寫
實(shí)際上就是重寫 true 方法,請看代碼

public class Foo
{
public Foo(int value)
{
_count = value;
}

private readonly int _count;

public static bool operator true(Foo mt)
{
return mt._count > 0;
}

public static bool operator false(Foo mt)
{
return mt._count < 0;
}
}

是不是覺得很多有人這樣寫,下面讓大家看一個很少人會知道的科技,感謝walterlv 提供
13、重寫運(yùn)算返回
很少人知道實(shí)際上重寫 == 可以返回任意的類型,而不是只有 bool 類型,請看下面代碼
是可以編譯通過的,因為我重寫運(yùn)算

class Foo
{
public int Count { get; set; }

public static string operator ==(Foo f1, Foo f2)
{
if (f1?.Count == f2?.Count)
{
return "lindexi";
}

return "";
}

public static string operator !=(Foo f1, Foo f2)
{
return "";
}
}

可以重寫的運(yùn)算很多,返回值可以自己隨意定義
14、await 任何類型
等待任意的類型,包括已定義的基礎(chǔ)類型,如下面代碼

await "林德熙逗比";

await "不告訴你";

這個代碼是可以編譯通過的,但是只有在我的設(shè)備。在看了這個博客之后,可能你也可以在你的設(shè)備編譯
其實(shí) await 是可以寫很多次的,如下面代碼

await await await await await await await await await await await await await await await await await await await await await await await "林德熙逗比";

15、變量名使用中文

實(shí)際上在C#支持所有 Unicode 字符,這是編譯器支持的,所以變量名使用中文也是可以的,而且可以使用特殊的字符

public string H\u00e5rf?ner()
{
return "可以編譯";
}

甚至以下的奇?zhèn)b代碼后續(xù)也許還能編過,當(dāng)然現(xiàn)在還是不行的哦

public class ?? : Exception { }
public static bool ??(this string value) => string.IsNullOrWhitespace(value);

string s = null;
if (s.??())
throw new ??();

16、if this == null

一般看到下面的代碼都覺得是不可能進(jìn)入輸出的

if (this == null) Console.WriteLine("this is null");

如果在 if 里面都能使用 this == null 成立,那么一定是vs炸了。實(shí)際上這個代碼還是可以運(yùn)行的
在一般的函數(shù),如下面的 Foo 函數(shù),在調(diào)用就需要使用f.Foo()的方法,方法里 this 就是 f 這個對象,如果 f == null 那么在調(diào)用方法就直接不讓運(yùn)行,如何到方法里的判斷

f.Foo(); //如果 f 為空,那么這里就不執(zhí)行

void Foo()
{
// 如果 this 為空,怎么可以調(diào)用這個方法
if (this == null) Console.WriteLine("this is null");
}

實(shí)際上是可以做的,請看(C#)if (this == null)?你在逗我,this 怎么可能為 null!用 IL 編譯和反編譯看穿一切 - walterlv 這篇博客
如上面博客,關(guān)鍵在修改 callvirtcall 調(diào)用,直接修改 IL 可以做出很多特殊的寫法
那么這個可以用在哪里?可以用在防止大神反編譯,如需要使用下面邏輯

//執(zhí)行的代碼

//不執(zhí)行的代碼

此時簡單的反編譯也許會這么寫

if(true)
{
//執(zhí)行的代碼
}
else
{
//不執(zhí)行的代碼
}

但是直接寫 true 很容易讓反編譯看到不使用代碼,而且在優(yōu)化代碼會被去掉,所以可以使用下面代碼

if(this == null)
{
//執(zhí)行的代碼
}
else
{
//不執(zhí)行的代碼
}

實(shí)際在微軟代碼也是這樣寫,點(diǎn)擊string的實(shí)現(xiàn)源代碼可以看到微軟代碼
17、重載的運(yùn)算符
實(shí)際上我可以將 null 強(qiáng)轉(zhuǎn)某個類,創(chuàng)建一個新的對象,請看代碼

Fantastic fantastic = (FantasticInfo) null;
fantastic.Foo();

這里的 FantasticInfo 和 Fantastic 沒有任何繼承關(guān)系,而且調(diào)用 Foo 不會出現(xiàn)空引用,也就是 fantastic 是從一個空的對象創(chuàng)建出來的
是不是覺得上面的科技很黑,實(shí)際原理沒有任何黑的科技,請看代碼

public class Fantastic
{
private Fantastic()
{
}

public static implicit operator Fantastic(FantasticInfo value) => new Fantastic();

public void Foo()
{
}
}

public class FantasticInfo
{
}

通過這個方式可以讓開發(fā)者無法直接創(chuàng)建 Fantastic 類,而且在不知道 FantasticInfo 的情況無法創(chuàng)建 Fantastic 也就是讓大家需要了解 FantasticInfo 才可以通過上面的方法創(chuàng)建,具體請看只有你能 new 出來!.NET 隱藏構(gòu)造函數(shù)的 n 種方法(Builder Pattern / 構(gòu)造器模式) - walterlv
課件鏈接:https://r302.cc/J4gxOX
當(dāng)然還有新的 C# 7.0 和 C# 8.0 的新的語法
例如下面的內(nèi)部方法返回自身
18、方法返回自身可以接近無限調(diào)用
有一天我看到了下面的代碼,你猜小伙伴用什么代碼定義了 Foo 這個代碼?

Foo()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()();

其實(shí)只需要定義一個委托,用內(nèi)部方法實(shí)現(xiàn)委托,因為內(nèi)部方法是可以返回自身,于是就可以使用5行代碼寫出 Foo 的定義

delegate Foo Foo(); // 定義委托

static void Main(string[] args)
{
Foo Foo() // 定義內(nèi)部方法
{
return Foo;
}
}

不過括號還不可以無限使用,因為編譯器有一個表達(dá)式的長度限制
19、無限長度的委托調(diào)用
試試這個代碼,也許你可以無限寫下去,只要 Roslyn 不會炸就可以

delegate Fx Fx(Fx fx);
Fx fx = fx => fx => fx => fx => fx => fx => fx => fx => fx => fx => fx => fx => fx => fx => fx => fx =>
fx => fx => fx => fx => fx => fx => fx => fx => fx => fx;

以下部分準(zhǔn)確來說是 .NET 提供的功能,請問 C# 和 .NET 是什么關(guān)系?其實(shí)我也無法用一兩句話說清,扔掉了 .NET 依然可以用 C# 寫程序,反過來扔掉 C# 也依然能用 .NET 寫程序
20、表達(dá)式樹獲取函數(shù)命名
定義一個類,下面通過表達(dá)式樹從類獲得函數(shù)命名

class Foo
{
public void KzcSevfio()
{
}
}
static void Main(string[] args)
{
GetMethodName<Foo>(foo => foo.KzcSevfio());
}

private static void GetMethodName<T>(Expression<Action<T>> action) where T : class
{
if (action.Body is MethodCallExpression expression)
{
Console.WriteLine(expression.Method.Name);
}
}

這樣就可以拿到函數(shù)的命名
21、DebuggerDisplay
如果想要在調(diào)試的時候,鼠標(biāo)移動到變量顯示他的信息,可以重寫類的 ToString

public sealed class Foo
{
public int Count { get; set; }

public override string ToString()
{
return Count.ToString();
}
}

但是如果 ToString 被其他地方用了,如何顯示?
微軟告訴大家,使用 DebuggerDisplay 特性

[DebuggerDisplay("{DebuggerDisplay}")]
public sealed class Foo
{
public int Count { get; set; }

private string DebuggerDisplay => $"(count {Count})";
}

他可以使用私有的屬性、字段,使用方法很簡單
參見Using the DebuggerDisplay Attribute
22、數(shù)字格式

string format = "000;-#;(0)";

string pos = 1.ToString(format); // 001
string neg = (-1).ToString(format); // -1
string zer = 0.ToString(format); // (0)

參見:自定義數(shù)字格式字符串
23、調(diào)用堆棧
如果需要獲得調(diào)用方法的堆棧,可以使用這個文章的方法

class Program
{
static void Main(string[] args)
{
var foo = new Foo();
foo.F1();
}
}

public sealed class Foo
{
public void F1()
{
F2();
}

void F2()
{
var stackTrace = new StackTrace();
var n = stackTrace.FrameCount;
for (int i = 0; i < n; i++)
{
Console.WriteLine(stackTrace.GetFrame(i).GetMethod().Name);
}
}
}

輸出

F2
F1

參見:WPF 判斷調(diào)用方法堆棧
調(diào)用堆棧判斷是存在一個小坑的,請看 dotnet 警惕使用 StackTrace 加獲取方法標(biāo)記 Attribute 特性在 Release 下被內(nèi)聯(lián)
歡迎加入 dotnet 職業(yè)技術(shù)學(xué)院 https://t.me/dotnet_campus 使用 Telegram 方法請看 如何使用 Telegram
24、在 try 和 finally 拋異常會發(fā)生什么
如下面代碼

private void F1()
{
try
{
A();
}
catch (Exception e)
{

}
}

private void A()
{
try
{
throw new ArgumentException("lindexi is doubi");
}
finally
{
throw new FileNotFoundException("lsj is doubi");
}
}

請問在 F1 的 catch 里面收到的 e 是什么類型,會觸發(fā)幾次?
答案請看 dotnet C# 在 finally 拋異常會發(fā)生什么
25、如果在構(gòu)造函數(shù)拋出異常 析構(gòu)函數(shù)是否會執(zhí)行
如下面代碼

private void F1()
{
try
{
_ = new Foo();
}
catch
{
// 忽略
}
}
class Foo
{
public Foo()
{
throw new Exception("lindexi is doubi");
}

~Foo()
{
}
}

請問以上代碼的 ~Foo 是否可以在垃圾回收執(zhí)行,或者說在構(gòu)造函數(shù)里面拋出異常,是否這個對象可以被垃圾回收答案請看 dotnet C# 如果在構(gòu)造函數(shù)拋出異常 析構(gòu)函數(shù)是否會執(zhí)行
26、只創(chuàng)建對象不調(diào)用構(gòu)造函數(shù)方法
通過 FormatterServices.GetUninitializedObject 方法可以只創(chuàng)建對象不調(diào)用構(gòu)造函數(shù)方法

Foo foo = null;
try
{
foo = (Foo) FormatterServices.GetUninitializedObject(typeof(Foo));
var constructorInfo = typeof(Foo).GetConstructor(new Type[0]);
constructorInfo!.Invoke(foo, null);
}
catch
{
}

class Foo
{

}

詳細(xì)請看 dotnet C# 只創(chuàng)建對象不調(diào)用構(gòu)造函數(shù)方法
最后
如果你覺得這篇文章對你有幫助,不妨點(diǎn)個贊支持一下!你的支持是我繼續(xù)分享知識的動力。如果有任何疑問或需要進(jìn)一步的幫助,歡迎隨時留言。

作者:lindexi

出處:https://blog.lindexi.com/post/C-很少人知道的科技.html
聲明:網(wǎng)絡(luò)內(nèi)容,僅供學(xué)習(xí),尊重版權(quán),侵權(quán)速刪,歉意致謝!


該文章在 2024/9/19 11:39:51 編輯過
關(guān)鍵字查詢
相關(guān)文章
正在查詢...
點(diǎn)晴ERP是一款針對中小制造業(yè)的專業(yè)生產(chǎn)管理軟件系統(tǒng),系統(tǒng)成熟度和易用性得到了國內(nèi)大量中小企業(yè)的青睞。
點(diǎn)晴PMS碼頭管理系統(tǒng)主要針對港口碼頭集裝箱與散貨日常運(yùn)作、調(diào)度、堆場、車隊、財務(wù)費(fèi)用、相關(guān)報表等業(yè)務(wù)管理,結(jié)合碼頭的業(yè)務(wù)特點(diǎn),圍繞調(diào)度、堆場作業(yè)而開發(fā)的。集技術(shù)的先進(jìn)性、管理的有效性于一體,是物流碼頭及其他港口類企業(yè)的高效ERP管理信息系統(tǒng)。
點(diǎn)晴WMS倉儲管理系統(tǒng)提供了貨物產(chǎn)品管理,銷售管理,采購管理,倉儲管理,倉庫管理,保質(zhì)期管理,貨位管理,庫位管理,生產(chǎn)管理,WMS管理系統(tǒng),標(biāo)簽打印,條形碼,二維碼管理,批號管理軟件。
點(diǎn)晴免費(fèi)OA是一款軟件和通用服務(wù)都免費(fèi),不限功能、不限時間、不限用戶的免費(fèi)OA協(xié)同辦公管理系統(tǒng)。
Copyright 2010-2025 ClickSun All Rights Reserved