C# 11 的新特性和改進前瞻

前言

.NET 7 的開發還剩下一個多月就要進入 RC,C# 11 的新特性和改進也即將敲定。在這個時間點上,不少新特性都已經實現完畢并合并入主分支

C# 11 包含的新特性和改進非常多,類型系統相比之前也有了很大的增強,在確保靜態類型安全的同時大幅提升了語言表達力。

那么本文就按照方向從 5 個大類來進行介紹,一起來提前看看 C# 11 的新特性和改進都有什么。

1. 類型系統的改進

抽象和虛靜態方法

C# 11 開始將?abstract?和?virtual?引入到靜態方法中,允許開發者在接口中編寫抽象和虛靜態方法。

接口與抽象類不同,接口用來抽象行為,通過不同類型實現接口來實現多態;而抽象類則擁有自己的狀態,通過各子類型繼承父類型來實現多態。這是兩種不同的范式。

在 C# 11 中,虛靜態方法的概念被引入,在接口中可以編寫抽象和虛靜態方法了。

interface IFoo{    // 抽象靜態方法abstract static int Foo1();    // 虛靜態方法virtual static int Foo2(){        return 42;}
}struct Bar : IFoo
{    // 隱式實現接口方法public static int Foo1(){        return 7;}
}Bar.Foo1(); // ok

由于運算符也屬于靜態方法,因此從 C# 11 開始,也可以用接口來對運算符進行抽象了。

interface ICanAdd<T> where T : ICanAdd<T>
{    abstract static T operator +(T left, T right);
}

這樣我們就可以給自己的類型實現該接口了,例如實現一個二維的點?Point

record struct Point(int X, int Y) : ICanAdd<Point>
{    // 隱式實現接口方法public static Point operator +(Point left, Point right){        return new Point(left.X + right.X, left.Y + right.Y);}
}

然后我們就可以對兩個?Point?進行相加了:

var p1 = new Point(1, 2);var p2 = new Point(2, 3);
Console.WriteLine(p1 + p2); // Point { X = 3, Y = 5 }

除了隱式實現接口之外,我們也可以顯式實現接口:

record struct Point(int X, int Y) : ICanAdd<Point>
{    // 顯式實現接口方法static Point ICanAdd<Point>.operator +(Point left, Point right){        return new Point(left.X + right.X, left.Y + right.Y);}
}

不過用顯示實現接口的方式的話,+?運算符沒有通過?public?公開暴露到類型?Point?上,因此我們需要通過接口來調用?+?運算符,這可以利用泛型約束來做到:

var p1 = new Point(1, 2);var p2 = new Point(2, 3);
Console.WriteLine(Add(p1, p2)); // Point { X = 3, Y = 5 }T Add<T>(T left, T right) where T : ICanAdd<T>{    return left + right;
}

對于不是運算符的情況,則可以利用泛型參數來調用接口上的抽象和靜態方法:

void CallFoo1<T>() where T : IFoo{T.Foo1();
}Bar.Foo1(); // errorCallFoo<Bar>(); // okstruct Bar : IFoo
{    // 顯式實現接口方法static void IFoo.Foo1(){        return 7;}
}

此外,接口可以基于另一個接口擴展,因此對于抽象和虛靜態方法而言,我們可以利用這個特性在接口上實現多態。

CallFoo<Bar1>(); // 5 5CallFoo<Bar2>(); // 6 4CallFoo<Bar3>(); // 3 7CallFooFromIA<Bar4>(); // 1CallFooFromIB<Bar4>(); // 2void CallFoo<T>() where T : IC{CallFooFromIA<T>();CallFooFromIB<T>();
}void CallFooFromIA<T>() where T : IA{Console.WriteLine(T.Foo());
}void CallFooFromIB<T>() where T : IB{Console.WriteLine(T.Foo());
}interface IA{    virtual static int Foo(){        return 1;}
}interface IB{    virtual static int Foo(){        return 2;}
}interface IC : IA, IB{    static int IA.Foo(){        return 3;}    static int IB.Foo(){        return 4;}
}struct Bar1 : IC
{    public static int Foo(){        return 5;}
}struct Bar2 : IC
{    static int IA.Foo(){        return 6;}
}struct Bar3 : IC
{    static int IB.Foo(){        return 7;}
}struct Bar4 : IA, IB { }折疊

同時,.NET 7 也利用抽象和虛靜態方法,對基礎庫中的數值類型進行了改進。在?System.Numerics?中新增了大量的用于數學的泛型接口,允許用戶利用泛型編寫通用的數學計算代碼:

using System.Numerics;V Eval<T, U, V>(T a, U b, V c) where T : IAdditionOperators<T, U, U>    where U : IMultiplyOperators<U, V, V>{    return (a + b) * c;
}Console.WriteLine(Eval(3, 4, 5)); // 35Console.WriteLine(Eval(3.5f, 4.5f, 5.5f)); // 44

泛型 attribute

C# 11 正式允許用戶編寫和使用泛型 attribute,因此我們可以不再需要使用?Type?來在 attribute 中存儲類型信息,這不僅支持了類型推導,還允許用戶通過泛型約束在編譯時就能對類型進行限制。

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]class FooAttribute<T> : Attribute where T : INumber<T>
{    public T Value { get; }    public FooAttribute(T v){Value = v;}
}[Foo<int>(3)] // ok[Foo<float>(4.5f)] // ok[Foo<string>("test")] // errorvoid MyFancyMethod() { }

ref 字段和 scoped ref

C# 11 開始,開發者可以在?ref struct?中編寫?ref?字段,這允許我們將其他對象的引用存儲在一個?ref struct?中:

int x = 1;
Foo foo = new(ref x);
foo.X = 2;
Console.WriteLine(x); // 2ref struct Foo
{    public ref int X;    public Foo(ref int x){X = ref x;}
}

可以看到,上面的代碼中將?x?的引用保存在了?Foo?中,因此對?foo.X?的修改會反映到?x?上。

如果用戶沒有對?Foo.X?進行初始化,則默認是空引用,可以利用?Unsafe.IsNullRef?來判斷一個?ref?是否為空:

ref struct Foo
{    public ref int X;    public bool IsNull => Unsafe.IsNullRef(ref X);    public Foo(ref int x){X = ref x;}
}

這里可以發現一個問題,那就是?ref field?的存在,可能會使得一個?ref?指向的對象的生命周期被擴展而導致錯誤,例如:

Foo MyFancyMethod(){    int x = 1;Foo foo = new(ref x);    return foo; // error}ref struct Foo
{    public Foo(ref int x) { }
}

上述代碼編譯時會報錯,因為?foo?引用了局部變量?x,而局部變量?x?在函數返回后生命周期就結束了,但是返回?foo?的操作使得?foo?的生命周期比?x?的生命周期更長,這會導致無效引用的問題,因此編譯器檢測到了這一點,不允許代碼通過編譯。

但是上述代碼中,雖然?foo?確實引用了?x,但是?foo?對象本身并沒有長期持有?x?的引用,因為在構造函數返回后就不再持有對?x?的引用了,因此這里按理來說不應該報錯。于是 C# 11 引入了?scoped?的概念,允許開發者顯式標注?ref?的生命周期,標注了?scoped?的?ref?表示這個引用的生命周期不會超過當前函數的生命周期:

Foo MyFancyMethod(){    int x = 1;Foo foo = new(ref x);    return foo; // ok}ref struct Foo
{    public Foo(scoped ref int x) { }
}

這樣一來,編譯器就知道?Foo?的構造函數不會使得?Foo?在構造函數返回后仍然持有?x?的引用,因此上述代碼就能安全通過編譯了。如果我們試圖讓一個?scoped ref?逃逸出當前函數的話,編譯器就會報錯:

ref struct Foo
{    public ref int X;    public Foo(scoped ref int x){X = ref x; // error}
}

如此一來,就實現了引用安全。

利用?ref?字段,我們可以很方便地實現各種零開銷設施,例如提供一個多種方法訪問顏色數據的?ColorView

using System.Diagnostics.CodeAnalysis;using System.Runtime.CompilerServices;using System.Runtime.InteropServices;var color = new Color { R = 1, G = 2, B = 3, A = 4 };
color.RawOfU32[0] = 114514;
color.RawOfU16[1] = 19198;
color.RawOfU8[2] = 10;
Console.WriteLine(color.A); // 74[StructLayout(LayoutKind.Explicit)]struct Color
{[FieldOffset(0)] public byte R;[FieldOffset(1)] public byte G;[FieldOffset(2)] public byte B;[FieldOffset(3)] public byte A;[FieldOffset(0)] public uint Rgba;    public ColorView<byte> RawOfU8 => new(ref this);    public ColorView<ushort> RawOfU16 => new(ref this);    public ColorView<uint> RawOfU32 => new(ref this);
}ref struct ColorView<T> where T : unmanaged{    private ref Color color;    public ColorView(ref Color color){        this.color = ref color;}[DoesNotReturn] private static ref T Throw() => throw new IndexOutOfRangeException();    public ref T this[uint index]{[MethodImpl(MethodImplOptions.AggressiveInlining)]        get{            unsafe{                return ref (sizeof(T) * index >= sizeof(Color) ?                    ref Throw() :                    ref Unsafe.Add(ref Unsafe.AsRef<T>(Unsafe.AsPointer(ref color)), (int)index));}}}
}

在字段中,ref?還可以配合?readonly?一起使用,用來表示不可修改的?ref,例如:

  • ref int:一個?int?的引用

  • readonly ref int:一個?int?的只讀引用

  • ref readonly int:一個只讀?int?的引用

  • readonly ref readonly int:一個只讀?int?的只讀引用

這將允許我們確保引用的安全,使得引用到只讀內容的引用不會被意外更改。

當然,C# 11 中的?ref?字段和?scoped?支持只是其完全形態的一部分,更多的相關內容仍在設計和討論,并在后續版本中推出。

文件局部類型

C# 11 引入了新的文件局部類型可訪問性符號?file,利用該可訪問性符號,允許我們編寫只能在當前文件中使用的類型:

// A.csfile class Foo{    // ...}file struct Bar
{    // ...}

如此一來,如果我們在與?Foo?和?Bar?的不同文件中使用這兩個類型的話,編譯器就會報錯:

// A.csvar foo = new Foo(); // okvar bar = new Bar(); // ok// B.csvar foo = new Foo(); // errorvar bar = new Bar(); // error

這個特性將可訪問性的粒度精確到了文件,對于代碼生成器等一些要放在同一個項目中,但是又不想被其他人接觸到的代碼而言將會特別有用。

required 成員

C# 11 新增了?required?成員,標記有?required?的成員將會被要求使用時必須要進行初始化,例如:

var foo = new Foo(); // errorvar foo = new Foo { X = 1 }; // okstruct Foo
{    public required int X;
}

開發者還可以利用?SetsRequiredMembers?這個 attribute 來對方法進行標注,表示這個方法會初始化?required?成員,因此用戶在使用時可以不需要再進行初始化:

using System.Diagnostics.CodeAnalysis;var p = new Point(); // errorvar p = new Point { X = 1, Y = 2 }; // okvar p = new Point(1, 2); // okstruct Point
{    public required int X;    public required int Y;[SetsRequiredMembers]    public Point(int x, int y){X = x;Y = y;}
}

利用?required?成員,我們可以要求其他開發者在使用我們編寫的類型時必須初始化一些成員,使其能夠正確地使用我們編寫的類型,而不會忘記初始化一些成員。

2. 運算改進

checked 運算符

C# 自古以來就有?checked?和?unchecked?概念,分別表示檢查和不檢查算術溢出:

byte x = 100;byte y = 200;unchecked{    byte z = (byte)(x + y); // ok}checked
{    byte z = (byte)(x + y); // error}

在 C# 11 中,引入了?checked?運算符概念,允許用戶分別實現用于?checked?和?unchecked?的運算符:

struct Foo
{    public static Foo operator +(Foo left, Foo right) { ... }    public static Foo operator checked +(Foo left, Foo right) { ... }
}var foo1 = new Foo(...);var foo2 = new Foo(...);var foo3 = unchecked(foo1 + foo2); // 調用 operator +var foo4 = checked(foo1 + foo2); // 調用 operator checked +

對于自定義運算符而言,實現?checked?的版本是可選的,如果沒有實現?checked?的版本,則都會調用?unchecked?的版本。

無符號右移運算符

C# 11 新增了?>>>?表示無符號的右移運算符。此前 C# 的右移運算符?>>?默認是有符號的右移,即:右移操作保留符號位,因此對于?int?而言,將會有如下結果:

1 >> 1 = -11 >> 2 = -11 >> 3 = -11 >> 4 = -1// ...

而新的?>>>?則是無符號右移運算符,使用后將會有如下結果:

1 >>> 1 = 21474836471 >>> 2 = 10737418231 >>> 3 = 5368709111 >>> 4 = 268435455// ...

這省去了我們需要無符號右移時,需要先將數值轉換為無符號數值后進行計算,再轉換回來的麻煩,也能避免不少因此導致的意外錯誤。

移位運算符放開類型限制

C# 11 開始,移位運算符的右操作數不再要求必須是?int,類型限制和其他運算符一樣被放開了,因此結合上面提到的抽象和虛靜態方法,允許我們聲明泛型的移位運算符了:

interface ICanShift<T> where T : ICanShift<T>
{    abstract static T operator <<(T left, T right);    abstract static T operator >>(T left, T right);
}

當然,上述的場景是該限制被放開的主要目的。然而,相信不少讀者讀到這里心中都可能會萌生一個邪惡的想法,沒錯,就是?cin?和?cout!雖然這種做法在 C# 中是不推薦的,但該限制被放開后,開發者確實能編寫類似的代碼了:

using static OutStream;using static InStream;int x = 0;
_ = cin >> To(ref x); // 有 _ = 是因為 C# 不允許運算式不經過賦值而單獨成為一條語句_ = cout << "hello" << " " << "world!";public class OutStream{    public static OutStream cout = new();    public static OutStream operator <<(OutStream left, string right){Console.WriteLine(right);        return left;}
}public class InStream{    public ref struct Ref<T>{        public ref T Value;        public Ref(ref T v) => Value = ref v;}    public static Ref<T> To<T>(ref T v) => new (ref v);    public static InStream cin = new();    public static InStream operator >>(InStream left, Ref<int> right){        var str = Console.Read(...);right.Value = int.Parse(str);}
}

IntPtr、UIntPtr 支持數值運算

C# 11 中,IntPtr?和?UIntPtr?都支持數值運算了,這極大的方便了我們對指針進行操作:

UIntPtr addr = 0x80000048;
IntPtr offset = 0x00000016;
UIntPtr newAddr = addr + (UIntPtr)offset; // 0x8000005E

當然,如同?Int32?和?intInt64?和?long?的關系一樣,C# 中同樣存在?IntPtr?和?UIntPtr?的等價簡寫,分別為?nint?和?nuint,n 表示 native,用來表示這個數值的位數和當前運行環境的內存地址位數相同:

nuint addr = 0x80000048;nint offset = 0x00000016;nuint newAddr = addr + (nuint)offset; // 0x8000005E

3. 模式匹配改進

列表模式匹配

C# 11 中新增了列表模式,允許我們對列表進行匹配。在列表模式中,我們可以利用?[ ]?來包括我們的模式,用?_?代指一個元素,用?..?代表 0 個或多個元素。在?..?后可以聲明一個變量,用來創建匹配的子列表,其中包含?..?所匹配的元素。

例如:

var array = new int[] { 1, 2, 3, 4, 5 };if (array is [1, 2, 3, 4, 5]) Console.WriteLine(1); // 1if (array is [1, 2, 3, ..]) Console.WriteLine(2); // 2if (array is [1, _, 3, _, 5]) Console.WriteLine(3); // 3if (array is [.., _, 5]) Console.WriteLine(4); // 4if (array is [1, 2, 3, .. var remaining])
{Console.WriteLine(remaining[0]); // 4Console.WriteLine(remaining.Length); // 2}

當然,和其他的模式一樣,列表模式同樣是支持遞歸的,因此我們可以將列表模式與其他模式組合起來使用:

var array = new string[] { "hello", ",", "world", "~" };if (array is ["hello", _, { Length: 5 }, { Length: 1 } elem, ..])
{Console.WriteLine(elem); // ~}

除了在?if?中使用模式匹配以外,在?switch?中也同樣能使用:

var array = new string[] { "hello", ",", "world", "!" };switch (array)
{    case ["hello", _, { Length: 5 }, { Length: 1 } elem, ..]:        // ...break;    default:        // ...break;
}var value = array switch{["hello", _, { Length: 5 }, { Length: 1 } elem, ..] => 1,_ => 2};Console.WriteLine(value); // 1

對 Span<char> 的模式匹配

在 C# 中,Span<char>?和?ReadOnlySpan<char>?都可以看作是字符串的切片,因此 C# 11 也為這兩個類型添加了字符串模式匹配的支持。例如:

int Foo(ReadOnlySpan<char> span){    if (span is "abcdefg") return 1;    return 2;
}Foo("abcdefg".AsSpan()); // 1Foo("test".AsSpan()); // 2

如此一來,使用?Span<char>?或者?ReadOnlySpan<char>?的場景也能夠非常方便地進行字符串匹配了,而不需要利用?SequenceEquals?或者編寫循環進行處理。

4. 字符串處理改進

原始字符串

C# 中自初便有?@?用來表示不需要轉義的字符串,但是用戶還是需要將?"?寫成?""?才能在字符串中包含引號。C# 11 引入了原始字符串特性,允許用戶利用原始字符串在代碼中插入大量的無需轉移的文本,方便開發者在代碼中以字符串的方式塞入代碼文本等。

原始字符串需要被至少三個?"?包裹,例如?"""?和?"""""?等等,前后的引號數量要相等。另外,原始字符串的縮進由后面引號的位置來確定,例如:

var str = """helloworld""";

此時?str?是:

hello
world

而如果是下面這樣:

var str = """helloworld
""";

str?則會成為:

helloworld

這個特性非常有用,例如我們可以非常方便地在代碼中插入 JSON 代碼了:

var json = """{"a": 1,"b": {"c": "hello","d": "world"},"c": [1, 2, 3, 4, 5]}""";
Console.WriteLine(json);/*
{"a": 1,"b": {"c": "hello","d": "world"},"c": [1, 2, 3, 4, 5]
}
*/

UTF-8 字符串

C# 11 引入了 UTF-8 字符串,我們可以用?u8?后綴來創建一個?ReadOnlySpan<byte>,其中包含一個 UTF-8 字符串:

var str1 = "hello world"u8; // ReadOnlySpan<byte>var str2 = "hello world"u8.ToArray(); // byte[]

UTF-8 對于 Web 場景而言非常有用,因為在 HTTP 協議中,默認編碼就是 UTF-8,而 .NET 則默認是 UTF-16 編碼,因此在處理 HTTP 協議時,如果沒有 UTF-8 字符串,則會導致大量的 UTF-8 和 UTF-16 字符串的相互轉換,從而影響性能。

有了 UTF-8 字符串后,我們就能非常方便的創建 UTF-8 字面量來使用了,不再需要手動分配一個?byte[]?然后在里面一個一個硬編碼我們需要的字符。

字符串插值允許換行

C# 11 開始,字符串的插值部分允許換行,因此如下代碼變得可能:

var str = $"hello, the leader is {group.GetLeader().GetName()}.";

這樣一來,當插值的部分代碼很長時,我們就能方便的對代碼進行格式化,而不需要將所有代碼擠在一行。

5. 其他改進

struct 自動初始化

C# 11 開始,struct?不再強制構造函數必須要初始化所有的字段,對于沒有初始化的字段,編譯器會自動做零初始化:

struct Point
{    public int X;    public int Y;    public Point(int x){X = x;        // Y 自動初始化為 0}
}

支持對其他參數名進行 nameof

C# 11 允許了開發者在參數中對其他參數名進行?nameof,例如在使用?CallerArgumentExpression?這一 attribute 時,此前我們需要直接硬編碼相應參數名的字符串,而現在只需要使用?nameof?即可:

void Assert(bool condition, [CallerArgumentExpression(nameof(condition))] string expression = "")
{    // ...}

這將允許我們在進行代碼重構時,修改參數名?condition?時自動修改?nameof?里面的內容,方便的同時減少出錯。

自動緩存靜態方法的委托

C# 11 開始,從靜態方法創建的委托將會被自動緩存,例如:

void Foo(){Call(Console.WriteLine);
}void Call(Action action){action();
}

此前,每執行一次?Foo,就會從?Console.WriteLine?這一靜態方法創建一個新的委托,因此如果大量執行?Foo,則會導致大量的委托被重復創建,導致大量的內存被分配,效率極其低下。在 C# 11 開始,將會自動緩存靜態方法的委托,因此無論?Foo?被執行多少次,Console.WriteLine?的委托只會被創建一次,節省了內存的同時大幅提升了性能。

總結

從 C# 8 開始,C# 團隊就在不斷完善語言的類型系統,在確保靜態類型安全的同時大幅提升語言表達力,從而讓類型系統成為編寫程序的得力助手,而不是礙手礙腳的限制。

本次更新還完善了數值運算相關的內容,使得開發者利用 C# 編寫數值計算方法時更加得心應手。

另外,模式匹配的探索旅程也終于接近尾聲,引入列表模式之后,剩下的就只有字典模式和活動模式了,模式匹配是一個非常強大的工具,允許我們像對字符串使用正則表達式那樣非常方便地對數據進行匹配。

總的來說 C# 11 的新特性和改進內容非常多,每一項內容都對 C# 的使用體驗有著不小的提升。在未來的 C# 中還計劃著角色和擴展等更加令人激動的新特性,讓我們拭目以待。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/284995.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/284995.shtml
英文地址,請注明出處:http://en.pswp.cn/news/284995.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

ajax加php實現三級聯動

js代碼 <script type"text/javascript"> function get_next(t,pid){ //當前元素的id&#xff0c;當前option的value&#xff0c;一般都是id吧&#xff1f;反正我的是 $.ajax({ type: "POST", url: "/index.p…

iOS 玩轉CocoaPods

####導語&#xff1a; 有時候看到其他人 source開源時候用pod xxx 配置在你的Podfile文件中&#xff0c;執行下pod install 或者 pod update &#xff0c;代碼瞬間就到你的pod庫, 頓時覺得高大上。那是怎么做到的呢&#xff1f; Agenda: CocoaPods 的由來Github 使用PodSpec介紹…

【ArcGIS Pro微課1000例】0015:ArcGIS Pro中屬性字段分式標注案例教程

文章目錄 1. 符號化2. 屬性字段分式標注在ArcGIS及Pro中很容易實現格式化標簽的,本文講解在ArcGIS Pro中實現屬性字段分式標注,結果如下圖所示: 1. 符號化 右鍵數據圖層→符號系統,打開符號系統對話框,住符號系統選擇【唯一值】,字段1選擇NAME。 2. 屬性字段分式標注 加…

mysql主從

1》mysql主從的工作原理&#xff1a;主服務器將更新寫入二進制日志文件&#xff08;bin_log&#xff09;&#xff0c;并維護文件的一個索引以跟蹤日志循環。這些日志可以記錄發送到從服務器的更新。當一個從服務器連接主服務器時&#xff0c;它通知 主服務器從服務器在日志中讀…

C語言試題184之編寫一個函數,從標準輸入讀取一個字符串,把字符串復制到動態內存分配的內存中,并返回該字符串的拷貝,這個函數不應該對讀入字符串的長度作任何限制

??個人主頁:個人主頁 ??系列專欄:C語言試題200例 ??推薦一款刷算法、筆試、面經、拿大公司offer神器?? 點擊跳轉進入網站 ?作者簡介:大家好,我是碼莎拉蒂,CSDN博客專家(全站排名Top 50),阿里云博客專家、51CTO博客專家、華為云享專家 1、題目 題目: 編寫一…

[轉]Linux面試題(2020最新版)

文章目錄 Linux 概述 什么是LinuxUnix和Linux有什么區別&#xff1f;什么是 Linux 內核&#xff1f;Linux的基本組件是什么&#xff1f;Linux 的體系結構BASH和DOS之間的基本區別是什么&#xff1f;Linux 開機啟動過程&#xff1f;Linux系統缺省的運行級別&#xff1f;Linux 使…

MSBuild 命令的簡單使用

MSBuild 命令的簡單使用獨立觀察員 2022 年 7 月 7 日位置在 VS 安裝目錄下&#xff0c;如&#xff1a;D:Microsoft Visual Studio2022EnterpriseMSBuildCurrentBin命令MSBuild 命令行參考 - MSBuild | Microsoft Docs&#xff08;https://docs.microsoft.com/zh-cn/visualstud…

ArcGIS實驗教程——實驗四十:ArcGIS洪水淹沒分析案例教程

文章目錄 一、洪水淹沒效果動畫演示二、實驗數據三、實驗過程1. 加載數據2. 符號化3. 夸大處理4. 動畫制作5. 動畫播放6. 導出動畫基于數字高程模型 ( DEM )格網模型,實現給定水深情況下洪水淹沒區的計算模型,討論洪水淹沒演進過程可視化實現的關鍵技術,以三維可視化方式,動…

數據庫顯示日期時僅僅顯示“年-月-日”的問題

日期時間實際上存放的是年-月-日 時&#xff1a;分&#xff1a;秒&#xff0c;但是僅僅顯示了年-月-日&#xff0c;未顯示時分秒信息。解決方法&#xff1a;工具-首選項-數據庫》NLS日期格式&#xff1a;YYYY-MM-DD HH24:MI:SS&#xff0c;保存后重新查詢。轉載于:https://blog…

C語言試題185之編寫calloc函數,函數內部使用malloc函數來獲取內存

??個人主頁:個人主頁 ??系列專欄:C語言試題200例 ??推薦一款刷算法、筆試、面經、拿大公司offer神器?? 點擊跳轉進入網站 ?作者簡介:大家好,我是碼莎拉蒂,CSDN博客專家(全站排名Top 50),阿里云博客專家、51CTO博客專家、華為云享專家 1、題目 題目: 編寫ca…

iOS11、iPhone X 適配簡單,但你的Apple思維適配做好了么?

2017.10.23 iPhone X 的劉海為什么這么丑&#xff1f; 如果喬布斯在的話就一定不會允許這樣的產品交付給用戶么&#xff1f; 作為 iOS 開發人員該對 Apple 有怎樣的認識&#xff1f; Apple 未來的發展的大方向是什么&#xff1f; 倘若沒有 Apple 的存在&#xff0c;我想各位現在…

【ArcGIS遇上Python】ArcGIS Python按照指定字段批量篩選不同類型的圖斑(以土地利用數據為例)

基于土地利用數據,根據用地類型名稱,批量篩選出不同類型的用地,生成不同類型的shp數據,以類型名稱命名。 文章目錄 1. 土地利用原始數據2. 根據名稱批量篩選結果3. ArcGIS Python批處理代碼ArcGIS Python根據字段屬性批量篩選生成shp圖層 1. 土地利用原始數據 2. 根據名稱批…

使用 StringZipper 壓縮、解壓字符串

數據壓縮是一個軟件開發中的常見需求&#xff1a;很多時候需要先將較大的數據進行壓縮然后再通過網絡等進行傳輸。在 .NET 中&#xff0c;有多個壓縮算法供我們選擇&#xff1a;Deflate、GZip 和 Br 。這些壓縮算法都是基于流&#xff08;Stream&#xff09;的&#xff0c;在對…

【轉載】【面試題】你是一個測試工程師,如何保證軟件質量?

2019獨角獸企業重金招聘Python工程師標準>>> *參*答*案&#xff1a;質量是有層次&#xff08;內部質量&#xff0c;外部質量&#xff0c;使用質量&#xff0c;過程質量&#xff09; 內部質量&#xff1a;主要指代碼的質量&#xff0c;就需要引入開發工程師進…

C語言試題186之讀取范圍在1和標準輸入讀取的size之前每個數據出現的次數

??個人主頁:個人主頁 ??系列專欄:C語言試題200例 ??推薦一款刷算法、筆試、面經、拿大公司offer神器?? 點擊跳轉進入網站 ?作者簡介:大家好,我是碼莎拉蒂,CSDN博客專家(全站排名Top 50),阿里云博客專家、51CTO博客專家、華為云享專家 1、題目 題目: 讀取范…

[轉]常見的用戶密碼加密方式以及破解方法

【作者】張輝&#xff0c;就職于攜程技術中心信息安全部&#xff0c;負責安全產品的設計與研發。 作為互聯網公司的信息安全從業人員經常要處理撞庫掃號事件&#xff0c;產生撞庫掃號的根本原因是一些企業發生了信息泄露事件&#xff0c;且這些泄露數據未加密或者加密方式比較弱…

H5學習筆記

什么是 HTML&#xff1f; HTML 是用來描述網頁的一種語言。 HTML 指的是超文本標記語言 (Hyper Text Markup Language)HTML 不是一種編程語言&#xff0c;而是一種標記語言 (markup language)標記語言是一套標記標簽 (markup tag)HTML 使用標記標簽來描述網頁關鍵字&#xff1a…

【ArcGIS遇上Python】ArcGIS Python獲取某個字段的唯一值(獲取指定字段的不同屬性值)

以土地利用數據為例,DLMC字段為每個圖斑的用地類型,怎樣用Python代碼獲取該字段的屬性唯一值? Python代碼實現結果: Python源代碼: import arcpy from arcpy import env arcpy.gp.overwriteOutput=

一張圖解決Android Studio 項目運行按鈕灰色

轉載于:https://juejin.im/post/5a31ee46f265da430406a166

java學習筆記8--接口總結

生活中的接口&#xff1a; 什么是接口? 一個Java接口是一些方法特征的集合&#xff0c;但沒有方法的實現。 在類中實現接口可以使用關鍵字implements&#xff0c;其基本格式如下&#xff1a; [修飾符] class <類名> [extends 父類名] [implements 接口列表]{}修飾符&…