Closed ufcpp closed 2 years ago
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
var a = new A { X = 0 };
Unsafe.As<A, int>(ref a) = 0x01020304;
Console.WriteLine(a.X);
Console.WriteLine(a.Equals(new A { X = 4 }));
// SkipLocalsInit とか P/Invoke とかがある以上、
// 使ってない「残り3バイト」を無視できない。
// なので、構造体の Equals は思ったよりも「単なる memcmp」じゃない。
[StructLayout(LayoutKind.Sequential, Pack = 4)]
struct A
{
public byte X;
}
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
var a = new A();
var b = new A();
// 使ってるビットだけをちゃんと比較してくれる。
// これの場合、最下位桁の 04。
Unsafe.As<A, int>(ref a) = 0x01020304;
Unsafe.As<A, int>(ref b) = 0x05060604;
Console.WriteLine(a.Equals(b));
[StructLayout(LayoutKind.Sequential, Pack = 4)]
record struct A(byte X);
using System.Runtime.CompilerServices;
var a = new B();
var b = new B();
// 隙間3バイトはちゃんと無視される。
Unsafe.As<B, ulong>(ref a) = 0x1234567801020304;
Unsafe.As<B, ulong>(ref b) = 0x1234567805060604;
Console.WriteLine(a.Equals(b));
record struct A(byte X); // 1バイト
record struct B(A X, int Y); // 8バイトで、XとYの間に3バイト隙間あり
Console.WriteLine(new Point1(1, 2)); // ToString からは X は消える。
Console.WriteLine(new Point1(1, 2).Equals(new(3, 2))); // Equals には private field も関与
record struct Point1(int X, int Y)
{
private int X = X;
public int Y = Y;
}
using System;
var anonymous = new { X = 1, Y = 2 };
Console.WriteLine(anonymous with { X = 3 }); // ildasm したらコンストラクター呼び出しに置き換わってた。
record struct A(int X); // これ、get; set; なので net48 とかでもコンパイル可能
// ↓この2つは自前定義の IsExternalInit が必要になる
record B(int X);
readonly record struct C(int X);
//namespace System.Runtime.CompilerServices
//{
// public class IsExternalInit { }
//}
using System;
var t = (X: 1, Y: 2) with { X = 3 };
// そもそも 1-tuple の (X: 1) がダメ。
// with は無関係。
var t1 = (X: 1) with { X = 3 };
#if false
const int N = 1;
class N { }
(N) // 既存コードではキャストの意味なので、今更 1-tuple にできない。
#endif
var t2 = ValueTuple.Create(1) with { Item1 = 2 }; // 行ける
void x = null; // これは null すらアウト、元がない
Unit x = null; // これはどんなに new を禁止しても null の1元はある
class Unit
{
private Unit() { }
}
// このコードは net48/C# 7 として合法。
// そこから LangVersion 10 で netstandard2.0 なライブラリを参照すると…
using System;
class Program
{
static void Main()
{
// ジェネリックな new() は、内部的には CreateInstance<T>() と一緒
Console.WriteLine(New<ClassLibrary1.A>().X); // 1
Console.WriteLine(New<ClassLibrary1.A>().X); // 0
Console.WriteLine(New<ClassLibrary1.A>().X); // 0
Console.WriteLine(New<ClassLibrary1.A>().X); // 0
}
static T New<T>()
where T : new()
=> new T();
}
var x = new PositiveInt();
Console.WriteLine(x.Value); // 1
x = default;
Console.WriteLine(x.Value); // 0
// 書けるのであれば時々書きたいことはなくはない。
struct PositiveInt
{
public int Value;
public PositiveInt() : this(1) { }
public PositiveInt(int x)
{
if (x <= 0) throw new ArgumentException(nameof(x));
Value = 1;
}
}
// default != new() なことを有用に使える場面はなくはない。
var array = new PositiveInt[] { new(1), new(2) };
var first = array.FirstOrDefault(x => x.Value > 3); // あえて条件満たさない
if (!first.IsDefault)
{
Console.WriteLine(first.Value);
}
else
{
Console.WriteLine("見つからなかったよ");
}
struct PositiveInt
{
public bool IsDefault => Value == 0;
public int Value;
public PositiveInt() : this(1) { }
public PositiveInt(int x)
{
if (x <= 0) throw new ArgumentException(nameof(x));
Value = 1;
}
}
実はすでに not-default 判定ロジックを持ってる。
non-defaultable 構造体が欲しい…
var x = NotNull<int?>(); // これはこの行が警告になることで not-null
var y = Defaultable<int?>(); // null
Console.WriteLine(y == null);
var z = Defaultable<int>(); // T? といいつつ not-null。0。defaultable。
Console.WriteLine(z == null); // false
T NotNull<T>() where T : notnull, new() => new();
T? Defaultable<T>() => default;
配信URL: https://youtu.be/gmFukCxLGc4
主に、record struct
と引数なしコンストラクター
ufcpp.net 向けにサンプルコード書き始めてる:
https://github.com/ufcpp/UfcppSample/tree/master/Demo/2021/Csharp10/RecordStruct https://github.com/ufcpp/UfcppSample/tree/master/Demo/2021/Csharp10/ParameterlessConstructors
記事書きトラッキング issue:
https://github.com/ufcpp/UfcppSample/issues/343 https://github.com/ufcpp/UfcppSample/issues/353
の話。
たぶん、default と Activator の深淵に落ちる。