С#, полученный из int32

У меня есть несколько классов с членами, называемыми "Id". Первоначально я хотел хранить их как ints, но я бы хотел, чтобы некоторый уровень защиты, чтобы я не случайно присваивал идентификатор комнаты человеку и т.д.

одно решение будет typedef (используя RoomId = System.Int32;), но тогда мне нужна эта строка кода во всех файлах, используя их. я предпочел бы, например, класс RoomId, полученный из int32, но я не могу понять, как его настроить, чтобы разрешить явное преобразование (для инициализации)

или я должен делать это другим способом?

Ответы

Ответ 1

Если я правильно понимаю, единственная операция, в которой вы действительно нуждаетесь, - это сравнение для равенства. Вы можете создать класс RoomId (или структуру, в зависимости от того, что вам подходит)

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;

Вы можете реализовать IEquatable, перегружать операторы равенства и неравенства, если хотите. Если вам нужна настойчивость, вы можете реализовать интерфейс ISerializable, чтобы убедиться, что целочисленное значение только "ускользает" от класса, если оно действительно необходимо.

Ответ 2

Вы не можете получить Int32, но вы можете указать неявные преобразования, которые могут дать вам поведение, которое вам нужно:

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

Ответ 3

 public static explicit operator RoomId(int value) {
     return new RoomId { Id = value };
 }

Затем вы можете:

 RoomId variable = (RoomId)10;

Невозможно получить класс из Int32 или любого другого типа значения в С#.

Ответ 4

Вы не можете наследовать типы значений (structs, включая Int32) в .NET.

Ближайшим вариантом было бы создать структуру, содержащую ничего, кроме вашего Int32. Это потребует того же пространства, но может быть ограничено вашей точной структурой. Затем вы можете изменить свою структуру позже, чтобы включить другую информацию, если это необходимо. (Помните, однако, что это, вероятно, будет нарушением API-интерфейса.)

Ответ 5

Мое предпочтение заключается в использовании простого шаблона T4 для генерации пользовательских типов "на лету". Вот пример, который я недавно использовал в личном проекте. В строке 10 представлен список типов, которые генерируются. Каждая из этих вещей была int, но теперь они строго типизированы.

Это также перемещает ваш код в сторону использования более функциональной парадигмы, избегая обычного анти-шаблона "примитивной одержимости".

Вот шаблон, который я использую. Не стесняйтесь обновлять/изменять (пусть остальные из нас знают, если вы добавите что-нибудь полезное).

<#@ 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}";
    }
<#
    }
#>