Permalink
Browse files

C# 7.2の例を 15.5 Preview 2 に合わせて修正

15.5 Preview 2 = 10/24 リリース版

ref readonly 引数が in に変わった
Span stackalloc の挙動がなんか変わった
`void X(int? x = default)` が 0 になるバグ直ったっぽい
  • Loading branch information...
ufcpp committed Oct 24, 2017
1 parent 9fd76cb commit be873e48c3bd7674f0ffbdf8923588bd72e95ff8
@@ -4,7 +4,7 @@ namespace ConsoleApp1
{
class Program
{
#if たぶんリリース版までには入るであろう物
#if false // 7.2 で入るかと思っていたら 7.X 行きになってた。それで確定だったらこのコード消す
static void RefLocalReassignment()
{
@@ -16,15 +16,16 @@ static void RefLocalReassignment()
r = 3;
}
#endif
static void BugFix()
{
void X(int? x = default) => Console.WriteLine(x);
X(); // C# 7.0 なぜか 0 になる。7.2 で直る。null に
X(); // C# 7.1 まではなぜか 0 になってた
X(default); // こっちは null
}
#endif
// さすがにひどいバグなので、破壊的変更とはいえ、ちゃんと X() で null になるように修正された
}
}
}
@@ -27,9 +27,8 @@ public LargeStruct(double a11, double a12, double a13, double a21, double a22, d
A33 = a33;
}
// 演算子の引数に ref readonly が使えるように
// この ref readonly は、たぶん、正式リリースまでに「in キーワードを使え」に変更される
public static LargeStruct operator +(ref readonly LargeStruct x, ref readonly LargeStruct y) => new LargeStruct(
// 演算子の引数に in (意味的には ref readonly) が使えるように
public static LargeStruct operator +(in LargeStruct x, in LargeStruct y) => new LargeStruct(
x.A11 + y.A11,
x.A12 + y.A12,
x.A13 + y.A13,
@@ -46,15 +45,16 @@ public LargeStruct(double a11, double a12, double a13, double a21, double a22, d
static class Ex
{
// ref this で、拡張メソッドに参照を渡せるように
// this ref の語順じゃない理由は謎
public static void Transpose(ref this LargeStruct x)
{
(x.A12, x.A21) = (x.A21, x.A12);
(x.A23, x.A32) = (x.A32, x.A23);
(x.A31, x.A13) = (x.A13, x.A31);
}
// 同上、ref readonly this で読み取り専用に
public static double Trace(ref readonly this LargeStruct x)
// 同上、in this で読み取り専用に
public static double Trace(in this LargeStruct x)
{
#if InvalidCode
x.A11 = 1; // 書き換え不可
@@ -78,7 +78,7 @@ static void Main()
Console.WriteLine(x); // [(1, 2, 3) / (4, 5, 6) / (7, 8, 9)]
// 演算子に対して ref readonly で渡る = コピーのコストがなくなる(※引数のみ。戻り値は相変わらずコピー)
// 演算子に対して int (ref readonly) で渡る = コピーのコストがなくなる(※引数のみ。戻り値は相変わらずコピー)
Console.WriteLine(x + y); // [(1, 1, 1) / (4, 4, 4) / (7, 7, 7)]
// 拡張メソッドだけど、x がちゃんと書き換わる
@@ -0,0 +1,67 @@
//#define InvalidCode
namespace ConsoleApp1.RefReadonly
{
class Program
{
static void Main()
{
RefReadonlyVar();
RefCall();
}
private static void RefReadonlyVar()
{
int x = 0;
// 参照ローカル変数を readonly にできるように
ref readonly int rr = ref x;
#if InvalidCode
// readonly なので、書き換えようとしたらエラーに
rr = 1;
#endif
// 将来的には (ref ではない) readonly int とかも追加される予定だったはずだけど
// (もしかしたら let キーワードとか使うかも)
// とりあえず ref readonly だけ先に入ったっぽい
}
// in で「参照渡しなんだけど x は書き換えれない」という意味
// 元々 out が「戻り値として使う意図で参照渡し」という意味なので、それとの対比で in キーワードを使うことに
//
// 参照ローカル変数の場合は ref readonly を使うんで、引数でも ref readonly にするかどうかはちょっと迷ったみたい。結局 in に
static void RefMethod(in int x)
{
#if InvalidCode
// readonly なので、書き換えようとしたらエラーに
x = 1;
#endif
}
// ref T と T でオーバーロードを作れるんだけど、in T と T でも可能
static void Overload(int x) { }
static void Overload(in int x) { }
private static void RefCall()
{
int x = 0;
// in (ref readonly) の場合、ref 修飾なしでメソッドを呼べる
//
// (readonly でない) ref の場合に ref 修飾が必要なのは、メソッドの中で書き換わる可能性があるのが怖いから
// 書き換わらないなら修飾の類は必要とされない
RefMethod(x);
// 一応、in 修飾を付けても OK。値渡しと参照渡しの弁別用
RefMethod(in x);
#if InvalidCode
// と言っても、今のバージョン(Preview 2。10/24版)ではこのコードはエラーに。たぶん、正式リリースまでには直る
Overload(x);
#endif
// こっちは OK
Overload(in x);
}
}
}
@@ -39,13 +39,22 @@ static unsafe byte FastButUnsafe(byte[] data)
static byte FastAndSafe(byte[] data)
{
#if false
//※ 今、一時的にこのコードがコンパイルできなくなってるっぽい
// 「Span<T> に ref readonly が付いてないと許さない」みたいな修正が入ってそうなんだけど、その一方で今ある Span<T> に ref readonly が付いてないとかそんなのだと思うんだけど。
// たぶん System.Memory パッケージの更新とかで直ると思うんだけど
// Span に対して代入するなら、unsafe を付けなくても stackalloc が使える
// counts.Length はちゃんと 256 だし、インデクサーの範囲チェックもされる = buffer over run 脆弱性とかは避けれる = 安全
Span<byte> counts = stackalloc byte[256];
foreach (var c in data) counts[c]++;
return Max(counts);
#else
return 0;
#endif
}
static byte Max(Span<byte> counts)

0 comments on commit be873e4

Please sign in to comment.