2009-05-11 4 views
13

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

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

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

ответ

7

Если я правильно понимаю, единственная операция, в которой вы действительно нуждаетесь, - это сравнение для равенства. Вы можете создать класс 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, чтобы убедиться, что целочисленное значение только «ускользает» от класса, если оно действительно необходимо.

+0

частное значение не имеет типа? это правильно? – bendewey

+0

@bendewey: это была опечатка. спасибо, что указали это. –

+0

отлично, спасибо! имеют последующие меры. так как нужно несколько разных идентификаторов, похоже, у меня должен быть класс master ID, который делает все это, а затем просто выводит subIds.Но это, похоже, не работает, как я думал. переименования выше ID и писать ParticipantID общественного класса: ID {} ​​ выдает ошибку: «ParticipantID не содержит конструктор, который принимает один аргумент» – second

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

Вы могли бы сделать:

RoomId variable = (RoomId)10; 

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

+0

Мне нужно получить к нему доступ с помощью myRoom.Id.id? Мне бы хотелось, чтобы он работал так, как будто это был int, т.е. просто myRoom.Id, уметь сравнивать их (что бы сравнить их значения и т. Д.) – second

+0

Нет, myRoom.Id будет работать. Вы также можете реализовать сопоставитель равенства, упомянутый в другом ответе. –

2

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

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

26

Вы не можете извлечь из 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 
+0

Отличное решение –

+0

Отличный, конечно более краткий, чем переопределение каждого метода и оператора. – Shocked

+0

Отличное решение. Я бы также добавил реализацию 'IEquatable'. – sdgfsdh

1

Я предпочитаю использовать простой шаблон 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}"; 
    } 
<# 
    } 
#> 
+0

Я никогда не слышал о шаблоне T4 перед вашим ответом. Определенно рад, что я наткнулся на него. Я сделал небольшое исправление, чтобы исправить ошибку компиляции, которую я получал. Несмотря на то, что это было достоверно, насколько мне показалось, мне не понравилось, как был создан конструктор, поэтому я использовал его более традиционный синтаксис. –

+0

Eagle-Eye, спасибо за вклад. Никаких проблем с изменением кода, но, честно говоря, я не уверен, в чем именно была ваша оппозиция. Если бы это был синтаксис C# 6, я бы ожидал, что вы также измените метод Equals(), а также остальные методы в нижней части класса. Зачем выделять конструктор для использования различного синтаксиса в частности? Я чувствую, что что-то упускаю. – zumalifeguard

+0

Как я уже сказал, компилятор бросает ошибки с конструктором, используя синтаксис C# 6, несмотря на то, что он действителен, насколько я могу судить. Intellisense говорит «public XDim (int value) => Value = value;' дает 'XDim.XDim (int) 'должен объявлять тело, потому что он не помечен как абстрактный, extern или partial',' Constructor должен иметь тело', и 'Имя 'Значение' не существует в текущем контексте. ', среди прочих. Странно, что у него нет проблем только с конструктором. VS2015 и .NET Framework 4.6.1, если это помогает сузить все. –

Смежные вопросы