私はこれが少し遅れるかもしれないことを知っています。しかし、これについてまだ他の方法を探している人にとっては、C# 標準のこの制限を回避する別の方法があるかもしれません。ラッパー クラス ReadOnly<T> where T : struct. を書くことができます。基本型 T への暗黙的な変換あり。ただし、wrapper<T> クラスへの明示的な変換のみ。開発者が暗黙的に ReadOnly<T> 型の値を設定しようとすると、コンパイラ エラーが強制されます。以下に 2 つの使用例を示します。
USAGE 1 呼び出し元の定義を変更する必要がありました。この使用法は、「TestCalled」関数コードの正確性をテストする場合にのみ使用されます。リリース レベル/ビルドでは使用しないでください。大規模な数学演算では、変換が過剰になり、コードが遅くなる可能性があるためです。私はそれを使用しませんが、デモンストレーションの目的でのみ投稿しました。
私が提案する USAGE 2 には、TestCalled2 関数でデモンストレーションされたデバッグとリリースの使用があります。また、このアプローチを使用する場合、TestCaller 関数に変換はありませんが、コンパイラの条件付けを使用して TestCaller2 定義のコーディングをもう少し行う必要があります。デバッグ構成ではコンパイラ エラーに気付くことができますが、リリース構成では TestCalled2 関数のすべてのコードが正常にコンパイルされます。
using System;
using System.Collections.Generic;
public class ReadOnly<VT>
where VT : struct
{
private VT value;
public ReadOnly(VT value)
{
this.value = value;
}
public static implicit operator VT(ReadOnly<VT> rvalue)
{
return rvalue.value;
}
public static explicit operator ReadOnly<VT>(VT rvalue)
{
return new ReadOnly<VT>(rvalue);
}
}
public static class TestFunctionArguments
{
static void TestCall()
{
long a = 0;
// CALL USAGE 1.
// explicite cast must exist in call to this function
// and clearly states it will be readonly inside TestCalled function.
TestCalled(a); // invalid call, we must explicit cast to ReadOnly<T>
TestCalled((ReadOnly<long>)a); // explicit cast to ReadOnly<T>
// CALL USAGE 2.
// Debug vs Release call has no difference - no compiler errors
TestCalled2(a);
}
// ARG USAGE 1.
static void TestCalled(ReadOnly<long> a)
{
// invalid operations, compiler errors
a = 10L;
a += 2L;
a -= 2L;
a *= 2L;
a /= 2L;
a++;
a--;
// valid operations
long l;
l = a + 2;
l = a - 2;
l = a * 2;
l = a / 2;
l = a ^ 2;
l = a | 2;
l = a & 2;
l = a << 2;
l = a >> 2;
l = ~a;
}
// ARG USAGE 2.
#if DEBUG
static void TestCalled2(long a2_writable)
{
ReadOnly<long> a = new ReadOnly<long>(a2_writable);
#else
static void TestCalled2(long a)
{
#endif
// invalid operations
// compiler will have errors in debug configuration
// compiler will compile in release
a = 10L;
a += 2L;
a -= 2L;
a *= 2L;
a /= 2L;
a++;
a--;
// valid operations
// compiler will compile in both, debug and release configurations
long l;
l = a + 2;
l = a - 2;
l = a * 2;
l = a / 2;
l = a ^ 2;
l = a | 2;
l = a & 2;
l = a << 2;
l = a >> 2;
l = ~a;
}
}