When the C# spec is incorporated into the docs.microsoft.com build, any links that include anchors generate warnings when the link is to the H1 tag in the first line of the file. These should be simple file links. This PR removes the extra anchor tags
618 lines
26 KiB
Markdown
618 lines
26 KiB
Markdown
# Structs
|
|
|
|
Structs are similar to classes in that they represent data structures that can contain data members and function members. However, unlike classes, structs are value types and do not require heap allocation. A variable of a struct type directly contains the data of the struct, whereas a variable of a class type contains a reference to the data, the latter known as an object.
|
|
|
|
Structs are particularly useful for small data structures that have value semantics. Complex numbers, points in a coordinate system, or key-value pairs in a dictionary are all good examples of structs. Key to these data structures is that they have few data members, that they do not require use of inheritance or referential identity, and that they can be conveniently implemented using value semantics where assignment copies the value instead of the reference.
|
|
|
|
As described in [Simple types](types.md#simple-types), the simple types provided by C#, such as `int`, `double`, and `bool`, are in fact all struct types. Just as these predefined types are structs, it is also possible to use structs and operator overloading to implement new "primitive" types in the C# language. Two examples of such types are given at the end of this chapter ([Struct examples](structs.md#struct-examples)).
|
|
|
|
## Struct declarations
|
|
|
|
A *struct_declaration* is a *type_declaration* ([Type declarations](namespaces.md#type-declarations)) that declares a new struct:
|
|
|
|
```antlr
|
|
struct_declaration
|
|
: attributes? struct_modifier* 'partial'? 'struct' identifier type_parameter_list?
|
|
struct_interfaces? type_parameter_constraints_clause* struct_body ';'?
|
|
;
|
|
```
|
|
|
|
A *struct_declaration* consists of an optional set of *attributes* ([Attributes](attributes.md)), followed by an optional set of *struct_modifier*s ([Struct modifiers](structs.md#struct-modifiers)), followed by an optional `partial` modifier, followed by the keyword `struct` and an *identifier* that names the struct, followed by an optional *type_parameter_list* specification ([Type parameters](classes.md#type-parameters)), followed by an optional *struct_interfaces* specification ([Partial modifier](structs.md#partial-modifier)) ), followed by an optional *type_parameter_constraints_clause*s specification ([Type parameter constraints](classes.md#type-parameter-constraints)), followed by a *struct_body* ([Struct body](structs.md#struct-body)), optionally followed by a semicolon.
|
|
|
|
### Struct modifiers
|
|
|
|
A *struct_declaration* may optionally include a sequence of struct modifiers:
|
|
|
|
```antlr
|
|
struct_modifier
|
|
: 'new'
|
|
| 'public'
|
|
| 'protected'
|
|
| 'internal'
|
|
| 'private'
|
|
| struct_modifier_unsafe
|
|
;
|
|
```
|
|
|
|
It is a compile-time error for the same modifier to appear multiple times in a struct declaration.
|
|
|
|
The modifiers of a struct declaration have the same meaning as those of a class declaration ([Class declarations](classes.md#class-declarations)).
|
|
|
|
### Partial modifier
|
|
|
|
The `partial` modifier indicates that this *struct_declaration* is a partial type declaration. Multiple partial struct declarations with the same name within an enclosing namespace or type declaration combine to form one struct declaration, following the rules specified in [Partial types](classes.md#partial-types).
|
|
|
|
### Struct interfaces
|
|
|
|
A struct declaration may include a *struct_interfaces* specification, in which case the struct is said to directly implement the given interface types.
|
|
|
|
```antlr
|
|
struct_interfaces
|
|
: ':' interface_type_list
|
|
;
|
|
```
|
|
|
|
Interface implementations are discussed further in [Interface implementations](interfaces.md#interface-implementations).
|
|
|
|
### Struct body
|
|
|
|
The *struct_body* of a struct defines the members of the struct.
|
|
|
|
```antlr
|
|
struct_body
|
|
: '{' struct_member_declaration* '}'
|
|
;
|
|
```
|
|
|
|
## Struct members
|
|
|
|
The members of a struct consist of the members introduced by its *struct_member_declaration*s and the members inherited from the type `System.ValueType`.
|
|
|
|
```antlr
|
|
struct_member_declaration
|
|
: constant_declaration
|
|
| field_declaration
|
|
| method_declaration
|
|
| property_declaration
|
|
| event_declaration
|
|
| indexer_declaration
|
|
| operator_declaration
|
|
| constructor_declaration
|
|
| static_constructor_declaration
|
|
| type_declaration
|
|
| struct_member_declaration_unsafe
|
|
;
|
|
```
|
|
|
|
Except for the differences noted in [Class and struct differences](structs.md#class-and-struct-differences), the descriptions of class members provided in [Class members](classes.md#class-members) through [Iterators](classes.md#iterators) apply to struct members as well.
|
|
|
|
## Class and struct differences
|
|
|
|
Structs differ from classes in several important ways:
|
|
|
|
* Structs are value types ([Value semantics](structs.md#value-semantics)).
|
|
* All struct types implicitly inherit from the class `System.ValueType` ([Inheritance](structs.md#inheritance)).
|
|
* Assignment to a variable of a struct type creates a copy of the value being assigned ([Assignment](structs.md#assignment)).
|
|
* The default value of a struct is the value produced by setting all value type fields to their default value and all reference type fields to `null` ([Default values](structs.md#default-values)).
|
|
* Boxing and unboxing operations are used to convert between a struct type and `object` ([Boxing and unboxing](structs.md#boxing-and-unboxing)).
|
|
* The meaning of `this` is different for structs ([This access](expressions.md#this-access)).
|
|
* Instance field declarations for a struct are not permitted to include variable initializers ([Field initializers](structs.md#field-initializers)).
|
|
* A struct is not permitted to declare a parameterless instance constructor ([Constructors](structs.md#constructors)).
|
|
* A struct is not permitted to declare a destructor ([Destructors](structs.md#destructors)).
|
|
|
|
### Value semantics
|
|
|
|
Structs are value types ([Value types](types.md#value-types)) and are said to have value semantics. Classes, on the other hand, are reference types ([Reference types](types.md#reference-types)) and are said to have reference semantics.
|
|
|
|
A variable of a struct type directly contains the data of the struct, whereas a variable of a class type contains a reference to the data, the latter known as an object. When a struct `B` contains an instance field of type `A` and `A` is a struct type, it is a compile-time error for `A` to depend on `B` or a type constructed from `B`. A struct `X` ***directly depends on*** a struct `Y` if `X` contains an instance field of type `Y`. Given this definition, the complete set of structs upon which a struct depends is the transitive closure of the ***directly depends on*** relationship. For example
|
|
```csharp
|
|
struct Node
|
|
{
|
|
int data;
|
|
Node next; // error, Node directly depends on itself
|
|
}
|
|
```
|
|
is an error because `Node` contains an instance field of its own type. Another example
|
|
```csharp
|
|
struct A { B b; }
|
|
|
|
struct B { C c; }
|
|
|
|
struct C { A a; }
|
|
```
|
|
is an error because each of the types `A`, `B`, and `C` depend on each other.
|
|
|
|
With classes, it is possible for two variables to reference the same object, and thus possible for operations on one variable to affect the object referenced by the other variable. With structs, the variables each have their own copy of the data (except in the case of `ref` and `out` parameter variables), and it is not possible for operations on one to affect the other. Furthermore, because structs are not reference types, it is not possible for values of a struct type to be `null`.
|
|
|
|
Given the declaration
|
|
```csharp
|
|
struct Point
|
|
{
|
|
public int x, y;
|
|
|
|
public Point(int x, int y) {
|
|
this.x = x;
|
|
this.y = y;
|
|
}
|
|
}
|
|
```
|
|
the code fragment
|
|
```csharp
|
|
Point a = new Point(10, 10);
|
|
Point b = a;
|
|
a.x = 100;
|
|
System.Console.WriteLine(b.x);
|
|
```
|
|
outputs the value `10`. The assignment of `a` to `b` creates a copy of the value, and `b` is thus unaffected by the assignment to `a.x`. Had `Point` instead been declared as a class, the output would be `100` because `a` and `b` would reference the same object.
|
|
|
|
### Inheritance
|
|
|
|
All struct types implicitly inherit from the class `System.ValueType`, which, in turn, inherits from class `object`. A struct declaration may specify a list of implemented interfaces, but it is not possible for a struct declaration to specify a base class.
|
|
|
|
Struct types are never abstract and are always implicitly sealed. The `abstract` and `sealed` modifiers are therefore not permitted in a struct declaration.
|
|
|
|
Since inheritance isn't supported for structs, the declared accessibility of a struct member cannot be `protected` or `protected internal`.
|
|
|
|
Function members in a struct cannot be `abstract` or `virtual`, and the `override` modifier is allowed only to override methods inherited from `System.ValueType`.
|
|
|
|
### Assignment
|
|
|
|
Assignment to a variable of a struct type creates a copy of the value being assigned. This differs from assignment to a variable of a class type, which copies the reference but not the object identified by the reference.
|
|
|
|
Similar to an assignment, when a struct is passed as a value parameter or returned as the result of a function member, a copy of the struct is created. A struct may be passed by reference to a function member using a `ref` or `out` parameter.
|
|
|
|
When a property or indexer of a struct is the target of an assignment, the instance expression associated with the property or indexer access must be classified as a variable. If the instance expression is classified as a value, a compile-time error occurs. This is described in further detail in [Simple assignment](expressions.md#simple-assignment).
|
|
|
|
### Default values
|
|
|
|
As described in [Default values](variables.md#default-values), several kinds of variables are automatically initialized to their default value when they are created. For variables of class types and other reference types, this default value is `null`. However, since structs are value types that cannot be `null`, the default value of a struct is the value produced by setting all value type fields to their default value and all reference type fields to `null`.
|
|
|
|
Referring to the `Point` struct declared above, the example
|
|
```csharp
|
|
Point[] a = new Point[100];
|
|
```
|
|
initializes each `Point` in the array to the value produced by setting the `x` and `y` fields to zero.
|
|
|
|
The default value of a struct corresponds to the value returned by the default constructor of the struct ([Default constructors](types.md#default-constructors)). Unlike a class, a struct is not permitted to declare a parameterless instance constructor. Instead, every struct implicitly has a parameterless instance constructor which always returns the value that results from setting all value type fields to their default value and all reference type fields to `null`.
|
|
|
|
Structs should be designed to consider the default initialization state a valid state. In the example
|
|
```csharp
|
|
using System;
|
|
|
|
struct KeyValuePair
|
|
{
|
|
string key;
|
|
string value;
|
|
|
|
public KeyValuePair(string key, string value) {
|
|
if (key == null || value == null) throw new ArgumentException();
|
|
this.key = key;
|
|
this.value = value;
|
|
}
|
|
}
|
|
```
|
|
the user-defined instance constructor protects against null values only where it is explicitly called. In cases where a `KeyValuePair` variable is subject to default value initialization, the `key` and `value` fields will be null, and the struct must be prepared to handle this state.
|
|
|
|
### Boxing and unboxing
|
|
|
|
A value of a class type can be converted to type `object` or to an interface type that is implemented by the class simply by treating the reference as another type at compile-time. Likewise, a value of type `object` or a value of an interface type can be converted back to a class type without changing the reference (but of course a run-time type check is required in this case).
|
|
|
|
Since structs are not reference types, these operations are implemented differently for struct types. When a value of a struct type is converted to type `object` or to an interface type that is implemented by the struct, a boxing operation takes place. Likewise, when a value of type `object` or a value of an interface type is converted back to a struct type, an unboxing operation takes place. A key difference from the same operations on class types is that boxing and unboxing copies the struct value either into or out of the boxed instance. Thus, following a boxing or unboxing operation, changes made to the unboxed struct are not reflected in the boxed struct.
|
|
|
|
When a struct type overrides a virtual method inherited from `System.Object` (such as `Equals`, `GetHashCode`, or `ToString`), invocation of the virtual method through an instance of the struct type does not cause boxing to occur. This is true even when the struct is used as a type parameter and the invocation occurs through an instance of the type parameter type. For example:
|
|
```csharp
|
|
using System;
|
|
|
|
struct Counter
|
|
{
|
|
int value;
|
|
|
|
public override string ToString() {
|
|
value++;
|
|
return value.ToString();
|
|
}
|
|
}
|
|
|
|
class Program
|
|
{
|
|
static void Test<T>() where T: new() {
|
|
T x = new T();
|
|
Console.WriteLine(x.ToString());
|
|
Console.WriteLine(x.ToString());
|
|
Console.WriteLine(x.ToString());
|
|
}
|
|
|
|
static void Main() {
|
|
Test<Counter>();
|
|
}
|
|
}
|
|
```
|
|
|
|
The output of the program is:
|
|
```
|
|
1
|
|
2
|
|
3
|
|
```
|
|
|
|
Although it is bad style for `ToString` to have side effects, the example demonstrates that no boxing occurred for the three invocations of `x.ToString()`.
|
|
|
|
Similarly, boxing never implicitly occurs when accessing a member on a constrained type parameter. For example, suppose an interface `ICounter` contains a method `Increment` which can be used to modify a value. If `ICounter` is used as a constraint, the implementation of the `Increment` method is called with a reference to the variable that `Increment` was called on, never a boxed copy.
|
|
|
|
```csharp
|
|
using System;
|
|
|
|
interface ICounter
|
|
{
|
|
void Increment();
|
|
}
|
|
|
|
struct Counter: ICounter
|
|
{
|
|
int value;
|
|
|
|
public override string ToString() {
|
|
return value.ToString();
|
|
}
|
|
|
|
void ICounter.Increment() {
|
|
value++;
|
|
}
|
|
}
|
|
|
|
class Program
|
|
{
|
|
static void Test<T>() where T: ICounter, new() {
|
|
T x = new T();
|
|
Console.WriteLine(x);
|
|
x.Increment(); // Modify x
|
|
Console.WriteLine(x);
|
|
((ICounter)x).Increment(); // Modify boxed copy of x
|
|
Console.WriteLine(x);
|
|
}
|
|
|
|
static void Main() {
|
|
Test<Counter>();
|
|
}
|
|
}
|
|
```
|
|
|
|
The first call to `Increment` modifies the value in the variable `x`. This is not equivalent to the second call to `Increment`, which modifies the value in a boxed copy of `x`. Thus, the output of the program is:
|
|
```
|
|
0
|
|
1
|
|
1
|
|
```
|
|
|
|
For further details on boxing and unboxing, see [Boxing and unboxing](types.md#boxing-and-unboxing).
|
|
|
|
### Meaning of this
|
|
|
|
Within an instance constructor or instance function member of a class, `this` is classified as a value. Thus, while `this` can be used to refer to the instance for which the function member was invoked, it is not possible to assign to `this` in a function member of a class.
|
|
|
|
Within an instance constructor of a struct, `this` corresponds to an `out` parameter of the struct type, and within an instance function member of a struct, `this` corresponds to a `ref` parameter of the struct type. In both cases, `this` is classified as a variable, and it is possible to modify the entire struct for which the function member was invoked by assigning to `this` or by passing this as a `ref` or `out` parameter.
|
|
|
|
### Field initializers
|
|
|
|
As described in [Default values](structs.md#default-values), the default value of a struct consists of the value that results from setting all value type fields to their default value and all reference type fields to `null`. For this reason, a struct does not permit instance field declarations to include variable initializers. This restriction applies only to instance fields. Static fields of a struct are permitted to include variable initializers.
|
|
|
|
The example
|
|
```csharp
|
|
struct Point
|
|
{
|
|
public int x = 1; // Error, initializer not permitted
|
|
public int y = 1; // Error, initializer not permitted
|
|
}
|
|
```
|
|
is in error because the instance field declarations include variable initializers.
|
|
|
|
### Constructors
|
|
|
|
Unlike a class, a struct is not permitted to declare a parameterless instance constructor. Instead, every struct implicitly has a parameterless instance constructor which always returns the value that results from setting all value type fields to their default value and all reference type fields to null ([Default constructors](types.md#default-constructors)). A struct can declare instance constructors having parameters. For example
|
|
```csharp
|
|
struct Point
|
|
{
|
|
int x, y;
|
|
|
|
public Point(int x, int y) {
|
|
this.x = x;
|
|
this.y = y;
|
|
}
|
|
}
|
|
```
|
|
|
|
Given the above declaration, the statements
|
|
```csharp
|
|
Point p1 = new Point();
|
|
Point p2 = new Point(0, 0);
|
|
```
|
|
both create a `Point` with `x` and `y` initialized to zero.
|
|
|
|
A struct instance constructor is not permitted to include a constructor initializer of the form `base(...)`.
|
|
|
|
If the struct instance constructor doesn't specify a constructor initializer, the `this` variable corresponds to an `out` parameter of the struct type, and similar to an `out` parameter, `this` must be definitely assigned ([Definite assignment](variables.md#definite-assignment)) at every location where the constructor returns. If the struct instance constructor specifies a constructor initializer, the `this` variable corresponds to a `ref` parameter of the struct type, and similar to a `ref` parameter, `this` is considered definitely assigned on entry to the constructor body. Consider the instance constructor implementation below:
|
|
```csharp
|
|
struct Point
|
|
{
|
|
int x, y;
|
|
|
|
public int X {
|
|
set { x = value; }
|
|
}
|
|
|
|
public int Y {
|
|
set { y = value; }
|
|
}
|
|
|
|
public Point(int x, int y) {
|
|
X = x; // error, this is not yet definitely assigned
|
|
Y = y; // error, this is not yet definitely assigned
|
|
}
|
|
}
|
|
```
|
|
|
|
No instance member function (including the set accessors for the properties `X` and `Y`) can be called until all fields of the struct being constructed have been definitely assigned. The only exception involves automatically implemented properties ([Automatically implemented properties](classes.md#automatically-implemented-properties)). The definite assignment rules ([Simple assignment expressions](variables.md#simple-assignment-expressions)) specifically exempt assignment to an auto-property of a struct type within an instance constructor of that struct type: such an assignment is considered a definite assignment of the hidden backing field of the auto-property. Thus, the following is allowed:
|
|
|
|
```csharp
|
|
struct Point
|
|
{
|
|
public int X { get; set; }
|
|
public int Y { get; set; }
|
|
|
|
public Point(int x, int y) {
|
|
X = x; // allowed, definitely assigns backing field
|
|
Y = y; // allowed, definitely assigns backing field
|
|
}
|
|
```
|
|
|
|
### Destructors
|
|
|
|
A struct is not permitted to declare a destructor.
|
|
|
|
### Static constructors
|
|
|
|
Static constructors for structs follow most of the same rules as for classes. The execution of a static constructor for a struct type is triggered by the first of the following events to occur within an application domain:
|
|
|
|
* A static member of the struct type is referenced.
|
|
* An explicitly declared constructor of the struct type is called.
|
|
|
|
The creation of default values ([Default values](structs.md#default-values)) of struct types does not trigger the static constructor. (An example of this is the initial value of elements in an array.)
|
|
|
|
## Struct examples
|
|
|
|
The following shows two significant examples of using `struct` types to create types that can be used similarly to the predefined types of the language, but with modified semantics.
|
|
|
|
### Database integer type
|
|
|
|
The `DBInt` struct below implements an integer type that can represent the complete set of values of the `int` type, plus an additional state that indicates an unknown value. A type with these characteristics is commonly used in databases.
|
|
|
|
```csharp
|
|
using System;
|
|
|
|
public struct DBInt
|
|
{
|
|
// The Null member represents an unknown DBInt value.
|
|
|
|
public static readonly DBInt Null = new DBInt();
|
|
|
|
// When the defined field is true, this DBInt represents a known value
|
|
// which is stored in the value field. When the defined field is false,
|
|
// this DBInt represents an unknown value, and the value field is 0.
|
|
|
|
int value;
|
|
bool defined;
|
|
|
|
// Private instance constructor. Creates a DBInt with a known value.
|
|
|
|
DBInt(int value) {
|
|
this.value = value;
|
|
this.defined = true;
|
|
}
|
|
|
|
// The IsNull property is true if this DBInt represents an unknown value.
|
|
|
|
public bool IsNull { get { return !defined; } }
|
|
|
|
// The Value property is the known value of this DBInt, or 0 if this
|
|
// DBInt represents an unknown value.
|
|
|
|
public int Value { get { return value; } }
|
|
|
|
// Implicit conversion from int to DBInt.
|
|
|
|
public static implicit operator DBInt(int x) {
|
|
return new DBInt(x);
|
|
}
|
|
|
|
// Explicit conversion from DBInt to int. Throws an exception if the
|
|
// given DBInt represents an unknown value.
|
|
|
|
public static explicit operator int(DBInt x) {
|
|
if (!x.defined) throw new InvalidOperationException();
|
|
return x.value;
|
|
}
|
|
|
|
public static DBInt operator +(DBInt x) {
|
|
return x;
|
|
}
|
|
|
|
public static DBInt operator -(DBInt x) {
|
|
return x.defined ? -x.value : Null;
|
|
}
|
|
|
|
public static DBInt operator +(DBInt x, DBInt y) {
|
|
return x.defined && y.defined? x.value + y.value: Null;
|
|
}
|
|
|
|
public static DBInt operator -(DBInt x, DBInt y) {
|
|
return x.defined && y.defined? x.value - y.value: Null;
|
|
}
|
|
|
|
public static DBInt operator *(DBInt x, DBInt y) {
|
|
return x.defined && y.defined? x.value * y.value: Null;
|
|
}
|
|
|
|
public static DBInt operator /(DBInt x, DBInt y) {
|
|
return x.defined && y.defined? x.value / y.value: Null;
|
|
}
|
|
|
|
public static DBInt operator %(DBInt x, DBInt y) {
|
|
return x.defined && y.defined? x.value % y.value: Null;
|
|
}
|
|
|
|
public static DBBool operator ==(DBInt x, DBInt y) {
|
|
return x.defined && y.defined? x.value == y.value: DBBool.Null;
|
|
}
|
|
|
|
public static DBBool operator !=(DBInt x, DBInt y) {
|
|
return x.defined && y.defined? x.value != y.value: DBBool.Null;
|
|
}
|
|
|
|
public static DBBool operator >(DBInt x, DBInt y) {
|
|
return x.defined && y.defined? x.value > y.value: DBBool.Null;
|
|
}
|
|
|
|
public static DBBool operator <(DBInt x, DBInt y) {
|
|
return x.defined && y.defined? x.value < y.value: DBBool.Null;
|
|
}
|
|
|
|
public static DBBool operator >=(DBInt x, DBInt y) {
|
|
return x.defined && y.defined? x.value >= y.value: DBBool.Null;
|
|
}
|
|
|
|
public static DBBool operator <=(DBInt x, DBInt y) {
|
|
return x.defined && y.defined? x.value <= y.value: DBBool.Null;
|
|
}
|
|
|
|
public override bool Equals(object obj) {
|
|
if (!(obj is DBInt)) return false;
|
|
DBInt x = (DBInt)obj;
|
|
return value == x.value && defined == x.defined;
|
|
}
|
|
|
|
public override int GetHashCode() {
|
|
return value;
|
|
}
|
|
|
|
public override string ToString() {
|
|
return defined? value.ToString(): "DBInt.Null";
|
|
}
|
|
}
|
|
```
|
|
|
|
### Database boolean type
|
|
|
|
The `DBBool` struct below implements a three-valued logical type. The possible values of this type are `DBBool.True`, `DBBool.False`, and `DBBool.Null`, where the `Null` member indicates an unknown value. Such three-valued logical types are commonly used in databases.
|
|
|
|
```csharp
|
|
using System;
|
|
|
|
public struct DBBool
|
|
{
|
|
// The three possible DBBool values.
|
|
|
|
public static readonly DBBool Null = new DBBool(0);
|
|
public static readonly DBBool False = new DBBool(-1);
|
|
public static readonly DBBool True = new DBBool(1);
|
|
|
|
// Private field that stores -1, 0, 1 for False, Null, True.
|
|
|
|
sbyte value;
|
|
|
|
// Private instance constructor. The value parameter must be -1, 0, or 1.
|
|
|
|
DBBool(int value) {
|
|
this.value = (sbyte)value;
|
|
}
|
|
|
|
// Properties to examine the value of a DBBool. Return true if this
|
|
// DBBool has the given value, false otherwise.
|
|
|
|
public bool IsNull { get { return value == 0; } }
|
|
|
|
public bool IsFalse { get { return value < 0; } }
|
|
|
|
public bool IsTrue { get { return value > 0; } }
|
|
|
|
// Implicit conversion from bool to DBBool. Maps true to DBBool.True and
|
|
// false to DBBool.False.
|
|
|
|
public static implicit operator DBBool(bool x) {
|
|
return x? True: False;
|
|
}
|
|
|
|
// Explicit conversion from DBBool to bool. Throws an exception if the
|
|
// given DBBool is Null, otherwise returns true or false.
|
|
|
|
public static explicit operator bool(DBBool x) {
|
|
if (x.value == 0) throw new InvalidOperationException();
|
|
return x.value > 0;
|
|
}
|
|
|
|
// Equality operator. Returns Null if either operand is Null, otherwise
|
|
// returns True or False.
|
|
|
|
public static DBBool operator ==(DBBool x, DBBool y) {
|
|
if (x.value == 0 || y.value == 0) return Null;
|
|
return x.value == y.value? True: False;
|
|
}
|
|
|
|
// Inequality operator. Returns Null if either operand is Null, otherwise
|
|
// returns True or False.
|
|
|
|
public static DBBool operator !=(DBBool x, DBBool y) {
|
|
if (x.value == 0 || y.value == 0) return Null;
|
|
return x.value != y.value? True: False;
|
|
}
|
|
|
|
// Logical negation operator. Returns True if the operand is False, Null
|
|
// if the operand is Null, or False if the operand is True.
|
|
|
|
public static DBBool operator !(DBBool x) {
|
|
return new DBBool(-x.value);
|
|
}
|
|
|
|
// Logical AND operator. Returns False if either operand is False,
|
|
// otherwise Null if either operand is Null, otherwise True.
|
|
|
|
public static DBBool operator &(DBBool x, DBBool y) {
|
|
return new DBBool(x.value < y.value? x.value: y.value);
|
|
}
|
|
|
|
// Logical OR operator. Returns True if either operand is True, otherwise
|
|
// Null if either operand is Null, otherwise False.
|
|
|
|
public static DBBool operator |(DBBool x, DBBool y) {
|
|
return new DBBool(x.value > y.value? x.value: y.value);
|
|
}
|
|
|
|
// Definitely true operator. Returns true if the operand is True, false
|
|
// otherwise.
|
|
|
|
public static bool operator true(DBBool x) {
|
|
return x.value > 0;
|
|
}
|
|
|
|
// Definitely false operator. Returns true if the operand is False, false
|
|
// otherwise.
|
|
|
|
public static bool operator false(DBBool x) {
|
|
return x.value < 0;
|
|
}
|
|
|
|
public override bool Equals(object obj) {
|
|
if (!(obj is DBBool)) return false;
|
|
return value == ((DBBool)obj).value;
|
|
}
|
|
|
|
public override int GetHashCode() {
|
|
return value;
|
|
}
|
|
|
|
public override string ToString() {
|
|
if (value > 0) return "DBBool.True";
|
|
if (value < 0) return "DBBool.False";
|
|
return "DBBool.Null";
|
|
}
|
|
}
|
|
```
|