2009-05-11 20 views
13

tengo varias clases con miembros llamados 'Id'. Originalmente quería almacenarlos como datos, pero me gustaría tener alguna capa de protección, para asegurarme de no asignar accidentalmente una identificación de habitación a una persona, etc.C# derivado de int32

una solución sería typedef (usando RoomId = System. Int32;) pero entonces necesito esa línea de código en todos los archivos que usan estos. Yo preferiría, por ej. una clase RoomId derivada de int32, pero no puedo encontrar la manera de configurarla para permitir la conversión explícita (para la inicialización)

o debería hacerlo de otra forma?

Respuesta

7

Si entiendo correctamente, la única operación que realmente necesita es la comparación para la igualdad. Puede crear una clase roomid (o estructura, lo que más le convenga)

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; 

se puede aplicar IEquatable, la sobrecarga de los operadores de igualdad y desigualdad si lo desea. Si necesita persistencia, podría implementar la interfaz ISerializable para asegurarse de que el valor entero solo "escapa" de la clase si realmente se necesita.

+0

private Value no tiene ningún tipo? ¿está bien? – bendewey

+0

@bendewey: eso fue un error tipográfico. Gracias por mencionarlo. –

+0

excelente, gracias! tenga un seguimiento. ya que necesito un par de id diferentes, parece que debería tener una clase de ID maestro que hace todo esto, y luego derivar subIds.Pero esto no parece funcionar como pensé. el cambio de nombre por encima de ID y escribir ParticipantID clase pública: ID {} ​​ da un error: "ParticipantID no contiene un constructor que toma 1 argumento" – second

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

entonces usted podría hacer:

RoomId variable = (RoomId)10; 

No es posible derivar una clase de Int32 o cualquier otro tipo de valor en C#.

+0

¿Tengo que acceder a ella usando myRoom.Id.id? me gustaría que funcione como si fuera un int, es decir, solo myRoom.Id, ser capaz de compararlos (lo que compararía sus valores, etc.) – second

+0

No, myRoom.Id funcionaría. También podría implementar el comparador de igualdad mencionado en la otra respuesta. –

2

No puede heredar tipos de valores (estructuras, incluyendo Int32) en .NET.

La opción más cercana sería hacer una estructura que no contenga nada más que su Int32. Esto requeriría el mismo espacio, pero podría restringirse a su estructura exacta. Luego, puede cambiar su estructura para incluir otra información, si es necesario. (Tenga en cuenta, sin embargo, que esto probablemente será un cambio de API de última hora).

26

No se puede derivar de Int32, pero puede especificar las conversiones implícitas, que podrían darle el comportamiento que necesita:

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

Excelente solución –

+0

Genial, ciertamente más conciso que anulando todos los métodos y operadores necesarios. – Shocked

+0

Gran solución. También agregaría una implementación 'IEquatable'. – sdgfsdh

1

Mi preferencia es utilizar una plantilla T4 sencilla de generar tipos personalizados en la mosca. Aquí hay un ejemplo que utilicé recientemente en un proyecto personal. En la línea 10, hay una lista de tipos que se generan. Cada una de estas cosas solía ser int, pero ahora están fuertemente tipadas.

Esto también mueve su código hacia el uso de un paradigma más funcional al evitar el habitual patrón de "obsesión primitiva".

Ahí está la plantilla que estoy usando. Siéntase libre de actualizar/modificar (deje que el resto de nosotros sepa si agrega algo útil).

<#@ 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

Nunca había oído hablar de plantillas T4 antes de su respuesta. Definitivamente contento de haberme encontrado con eso. Hice una pequeña edición para corregir un error de compilación que estaba recibiendo. A pesar de ser válido por lo que pude ver, no pareció gustarle la forma en que se configuró el constructor, por lo que hice que utilizara la sintaxis más tradicional. –

+0

Eagle-Eye, gracias por la contribución. No hay problema con el cambio de código, pero para ser sincero, no estoy seguro de cuál fue exactamente tu oposición. Si fuera por la sintaxis C# 6, entonces esperaba que también cambiaras el método Equals(), así como el resto de los métodos al final de la clase. ¿Por qué identificar al constructor para usar la sintaxis diferente en particular? Siento que me estoy perdiendo algo. – zumalifeguard

+0

Como dije, el compilador lanzaba errores con el constructor usando la sintaxis C# 6, a pesar de que es válido por lo que puedo decir. Intellisense dice 'public XDim (int value) => Value = valor;' da ''XDim.XDim (int)' debe declarar un cuerpo porque no está marcado como abstracto, externo o parcial',' Constructor debe tener cuerpo', y 'El nombre 'Valor' no existe en el contexto actual., entre otros. Extraño, no tiene problema solo con el constructor. VS2015 y .NET Framework 4.6.1, si eso ayuda a reducir cualquier cosa. –