简体   繁体   中英

c# deriving from int32

i have several classes with members called 'Id'. Originally i wanted to store these as ints, but i would like some layer of protection, to make sure i don't accidentally assign a room id to a person etc.

one solution would be typedef (using RoomId = System.Int32;) but then i need that line of code in all files using these. i would prefer eg a RoomId class derived from int32, but i can't figure out how to set it up to allow explicit conversion (for initilisation)

or should i do this in some other way?

You can't derive from Int32 , but you can specify implicit conversions, which might give you the behaviour you need:

public struct RoomId
{
    private int _Value;

    public static implicit operator RoomId(int value)
    {
        return new RoomId { _Value = value };
    }

    public static implicit operator int(RoomId value)
    {
        return value._Value;
    }
}

// ...

RoomId id = 42;

Console.WriteLine(id == 41);    // False
Console.WriteLine(id == 42);    // True
Console.WriteLine(id < 42);     // False
Console.WriteLine(id > 41);     // True
Console.WriteLine(id * 2);      // 84
 public static explicit operator RoomId(int value) {
     return new RoomId { Id = value };
 }

You could then do:

 RoomId variable = (RoomId)10;

It's not possible to derive a class from Int32 or any other value type in C#.

If I understand correctly, the only operation you really need is comparison for equality. You can create a RoomId class (or struct, whichever suits you)

class RoomId
{
    private int Value {get; set;}

    public RoomId(int value)
    {
        this.Value = value;
    }

    public bool Equals(RoomId other)
    {
        return this.Value == other.Value;
    }
}

RoomId room1 = new RoomId(1);
RoomId room2 = new RoomId(2);

// To compare for equality
bool isItTheSameRoom = room1.Equals(room2);
// Or if you have overloaded the equality operator (==)
bool isItTheSameRoom = room1 == room2;

You can implement IEquatable, overload the equality and inequality operators if you want. If you need persistence, you could implement the ISerializable interface to make sure that the integer value only "escapes" the class if it is really needed.

You cannot inherit value types (structs, including Int32) in .NET.

The closest option would be to make a struct which contained nothing but your Int32. This would require the same space, but could be restricted to your exact struct. You could then change your struct later to include other information, if needed. (Be aware, though, that this will probably be a breaking API change.)

My preference is to use a simple T4 template to generate custom types on the fly. Here's an example I used on a personal project recently. On line 10, is a list of types that are generated. Each of these things used to be int , but now they're strongly typed.

This also moves your code toward using a more Functional paradigm by avoiding the usual "primitive obsession" anti-pattern.

There's the template I'm using. Feel free to update/modify (let the rest of us know if you add anything useful).

<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #>
<#

// List of types to generate:
var createTypeList = new[] { "XDim", "YDim", "YDelta", "DelayValue", "HValue", "Score", "TplIndexValue" };

#>
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.Contracts;
// ReSharper disable CheckNamespace

<#
    for(int i = 0; i < createTypeList.Length; i++) 
    {
        var typeName = createTypeList[i];
#>

    [ImmutableObject(true)]
    public struct <#=typeName#> : IComparable<<#=typeName#>>
    {
        public <#=typeName#>(int value) { Value = value; }

        [Pure] public int Value { get; }
        [Pure] public bool Equals(<#=typeName#> other) => Value == other.Value;

        [Pure]
        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (Equals(this, obj)) return true;
            return obj.GetType() == GetType() && Equals((<#=typeName#>)obj);
        }

        [Pure]
        public override int GetHashCode()
        {
            unchecked
            {
                return (base.GetHashCode() * 397) ^ Value;
            }
        }

        [Pure] public static bool operator ==(<#=typeName#> left, <#=typeName#> right) => Equals(left, right);
        [Pure] public static bool operator !=(<#=typeName#> left, <#=typeName#> right) => !Equals(left, right);
        [Pure] public int CompareTo(<#=typeName#> other) => Equals(this, other) ? 0 : Value.CompareTo(other.Value);
        [Pure] public static bool operator <(<#=typeName#> left, <#=typeName#> right) => Comparer<<#=typeName#>>.Default.Compare(left, right) < 0;
        [Pure] public static bool operator >(<#=typeName#> left, <#=typeName#> right) => Comparer<<#=typeName#>>.Default.Compare(left, right) > 0;
        [Pure] public static bool operator <=(<#=typeName#> left, <#=typeName#> right) => Comparer<<#=typeName#>>.Default.Compare(left, right) <= 0;
        [Pure] public static bool operator >=(<#=typeName#> left, <#=typeName#> right) => Comparer<<#=typeName#>>.Default.Compare(left, right) >= 0;
        [Pure] public override string ToString() => $"{nameof(Value)}: {Value}";
    }
<#
    }
#>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM