这个 class 结构能更好地表示吗?
Can this class structure be better represented?
public class Foo<T> { internal Foo() { } }
public sealed class Foo_SByte : Foo<SByte> { }
public sealed class Foo_Byte : Foo<Byte> { }
public sealed class Foo_Short : Foo<Int16> { }
public sealed class Foo_UShort : Foo<UInt16> { }
public sealed class Foo_Int : Foo<Int32> { }
public sealed class Foo_UInt : Foo<UInt32> { }
public sealed class Foo_Long : Foo<Int64> { }
public sealed class Foo_ULong : Foo<UInt64> { }
public sealed class Foo_Char : Foo<Char> { }
public sealed class Foo_Float : Foo<Single> { }
public sealed class Foo_Double : Foo<Double> { }
public sealed class Foo_Bool : Foo<Boolean> { }
public sealed class Foo_Decimal : Foo<Decimal> { }
public sealed class Foo_String: Foo<string> { }
public class Foo_Struct<T> : Foo<T> where T : struct { }
public class Foo_Enum<T> : Foo<T> where T : Enum { }
// Now T is immutable
我的 objective 是将 Foo 的使用限制在所有潜在的值类型上,我在语义上(可能不正确地)假设这等同于说“所有不可变类型”
换句话说,我不希望 T 成为引用类型。
实现在 Bars
之间没有变化。我只希望 T 的保证仅限于不可变类型。
我有没有漏掉任何东西(不包括我知道的简单类型)?
有没有更好的方法?
编辑:好的,我完全实现了。现在 Foo<T>
的所有使用都保证是不可变的。 (如果没有,请告诉我我错过了什么类型。或者尝试从 Foo 继承来证明我错了)。
无法在编译时或运行时检查不变性。您可以获得的最接近的是检测类型是否具有复制语义(也就是说,如果分配 x = y
将导致 x
具有独立于 y
被更改的值 - 排序of,请继续阅读),因为有效地具有(内在)复制语义的类型列表在 C# 中受到限制。所以你可以进行运行时检查:
class Foo<T> {
public Foo() {
if (!(typeof(T).IsValueType || typeof(T) == typeof(string))) {
throw new ArgumentException($"{typeof(T)} does not have copy semantics.");
}
}
}
从概念上讲,每次实例化 Foo<T>
时都会进行此检查,但实际上 JIT 可以优化它。
请注意,仅具有复制语义仍然不能保证状态的正确性。特别是,user-defined 值类型可以引用实例外部的状态。简单示例:
struct SneakyStruct {
static int i = 0;
public int Value => i++;
}
您可以复制 SneakyStruct
并且无法从外部修改状态这一事实仍然无助于保证您每次都会获得相同的 Value
。当然,这个例子是故意反常的,但还是要注意不要依赖你不必依赖的东西,或者实际上不能依赖的东西。
public class Foo<T> { internal Foo() { } }
public sealed class Foo_SByte : Foo<SByte> { }
public sealed class Foo_Byte : Foo<Byte> { }
public sealed class Foo_Short : Foo<Int16> { }
public sealed class Foo_UShort : Foo<UInt16> { }
public sealed class Foo_Int : Foo<Int32> { }
public sealed class Foo_UInt : Foo<UInt32> { }
public sealed class Foo_Long : Foo<Int64> { }
public sealed class Foo_ULong : Foo<UInt64> { }
public sealed class Foo_Char : Foo<Char> { }
public sealed class Foo_Float : Foo<Single> { }
public sealed class Foo_Double : Foo<Double> { }
public sealed class Foo_Bool : Foo<Boolean> { }
public sealed class Foo_Decimal : Foo<Decimal> { }
public sealed class Foo_String: Foo<string> { }
public class Foo_Struct<T> : Foo<T> where T : struct { }
public class Foo_Enum<T> : Foo<T> where T : Enum { }
// Now T is immutable
我的 objective 是将 Foo 的使用限制在所有潜在的值类型上,我在语义上(可能不正确地)假设这等同于说“所有不可变类型”
换句话说,我不希望 T 成为引用类型。
实现在 Bars
之间没有变化。我只希望 T 的保证仅限于不可变类型。
我有没有漏掉任何东西(不包括我知道的简单类型)?
有没有更好的方法?
编辑:好的,我完全实现了。现在 Foo<T>
的所有使用都保证是不可变的。 (如果没有,请告诉我我错过了什么类型。或者尝试从 Foo 继承来证明我错了)。
无法在编译时或运行时检查不变性。您可以获得的最接近的是检测类型是否具有复制语义(也就是说,如果分配 x = y
将导致 x
具有独立于 y
被更改的值 - 排序of,请继续阅读),因为有效地具有(内在)复制语义的类型列表在 C# 中受到限制。所以你可以进行运行时检查:
class Foo<T> {
public Foo() {
if (!(typeof(T).IsValueType || typeof(T) == typeof(string))) {
throw new ArgumentException($"{typeof(T)} does not have copy semantics.");
}
}
}
从概念上讲,每次实例化 Foo<T>
时都会进行此检查,但实际上 JIT 可以优化它。
请注意,仅具有复制语义仍然不能保证状态的正确性。特别是,user-defined 值类型可以引用实例外部的状态。简单示例:
struct SneakyStruct {
static int i = 0;
public int Value => i++;
}
您可以复制 SneakyStruct
并且无法从外部修改状态这一事实仍然无助于保证您每次都会获得相同的 Value
。当然,这个例子是故意反常的,但还是要注意不要依赖你不必依赖的东西,或者实际上不能依赖的东西。