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.mzph.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 接口列表]{}修饰符&…