2009-05-11 15 views
13

Mam kilka klas z członkami o nazwie "Id". Pierwotnie chciałem przechowywać te pliki jako ints, ale chciałbym mieć jakąś warstwę ochronną, aby upewnić się, że nie przypisze się przypadkowo identyfikatora pokoju do osoby itp.C# pochodzące z Int32

Jednym z rozwiązań będzie typedef (przy użyciu RoomId = System. Int32;), ale potrzebuję tej linii kodu we wszystkich plikach, które ją używają. wolałbym np. klasa RoomId wywodząca się z klasy int32, ale nie wiem, jak ją skonfigurować, aby umożliwić jawną konwersję (w celu inicjalizacji):

czy powinienem to zrobić w jakiś inny sposób?

Odpowiedz

7

Jeśli dobrze rozumiem, jedyną czynnością, której naprawdę potrzebujesz, jest porównanie pod kątem równości. Można utworzyć klasę RoomId (lub struct, cokolwiek pasuje do Ciebie)

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; 

można wdrożyć IEquatable, przeciążanie operatorów równości i nierówności, jeśli chcesz. Jeśli potrzebujesz uporczywości, możesz zaimplementować interfejs ISerializable, aby upewnić się, że wartość całkowita "wymyka się" z klasy, jeśli jest naprawdę potrzebna.

+0

private Wartość nie ma czcionki? czy to prawda? – bendewey

+0

@bendewey: to był błąd literowy. dzięki za wskazanie. –

+0

świetnie, dzięki! mają uzupełnienie. ponieważ potrzebuję kilku różnych identyfikatorów, wydaje mi się, że powinienem mieć klasę master-master, która to wszystko robi, a następnie wyprowadzać podkategorie.Ale wydaje się, że to nie działa tak jak myślałem. zmiana nazwy powyżej ID i pisanie publicznej klasy ParticipantID: ID {} ​​ daje błąd: „ParticipantID nie zawiera konstruktor, że trwa 1 argument” – second

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

Można wtedy zrobić:

RoomId variable = (RoomId)10; 

To nie jest możliwe określenie klasy z Int32 lub innego rodzaju wartości w C#.

+0

czy muszę uzyskać do niego dostęp za pomocą pliku myRoom.Id.id? Chciałbym, żeby działało tak, jakby było int, czyli po prostu myRoom.Id, umieć je porównać (co by porównać ich wartości itp.) – second

+0

Nie, myRoom.Id zadziała. Można również zaimplementować porównywarkę równości wymienioną w drugiej odpowiedzi. –

2

Nie można dziedziczyć typów wartości (struktur, w tym Int32) w .NET.

Najbliższą opcją byłoby utworzenie struktury, która nie zawierała nic oprócz twojego Int32. Wymagałoby to tego samego miejsca, ale może być ograniczone do twojej dokładnej struktury. Następnie możesz później zmienić strukturę, aby w razie potrzeby uwzględnić inne informacje. (Pamiętaj jednak, że prawdopodobnie będzie to zmiana API).

26

Nie można czerpać z Int32, ale można określić niejawne konwersje, które może dać zachowanie co potrzeba:

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

Doskonałe rozwiązanie –

+0

Świetne, na pewno bardziej zwięzłe niż przesłonięcie każdej metody i operatora. – Shocked

+0

Świetne rozwiązanie. Dodałbym również implementację 'IEquatable'. – sdgfsdh

1

Moje preferencja jest użycie prostego szablonu T4 generować niestandardowe typy na latać. Oto przykład, który ostatnio wykorzystałem w prywatnym projekcie. W linii 10 znajduje się lista generowanych typów. Każda z tych rzeczy kiedyś była int, ale teraz są mocno wpisane.

Przenosi to również twój kod w kierunku bardziej funkcjonalnego paradygmatu, unikając zwykłej antyparty "prymitywnej obsesji".

Istnieje szablon, którego używam. Zapraszam do aktualizacji/modyfikacji (niech reszta z nas wie, czy dodasz coś użytecznego).

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

Właściwie nigdy nie słyszałem o szablonach T4 przed twoją odpowiedzią. Zdecydowanie się cieszę, że natknąłem się na to. Zrobiłem małą zmianę, aby naprawić błąd kompilacji, który otrzymywałem. Pomimo tego, że jest on ważny, o ile mogłem powiedzieć, nie podobało mu się to, w jaki sposób konstruktor został skonfigurowany, więc wykorzystałem bardziej tradycyjną składnię. –

+0

Orzeł, dziękuję za wkład. Żaden problem związany ze zmianą kodu, ale szczerze mówiąc, nie jestem pewien, jaka była twoja opozycja. Gdyby była to składnia C# 6, oczekiwałbym, że zmienisz także metodę Equals(), a także resztę metod na dole klasy. Po co wyróżniać konstruktora, aby w szczególności używać różnych składni? Czuję, że czegoś brakuje. – zumalifeguard

+0

Tak jak powiedziałem, kompilator generował błędy przy użyciu konstruktora przy użyciu składni C# 6, mimo że jest on ważny, o ile wiem. Intellisense mówi: 'public XDim (wartość int) => Value = value;' daje ''XDim.XDim (int)' musi zadeklarować ciało, ponieważ nie jest ono oznaczone jako abstract, extern lub partial',' Constructor musi body ", oraz 'Nazwa 'Wartość' nie istnieje w bieżącym kontekście.", między innymi. Dziwne, że nie ma problemu tylko z konstruktorem. VS2015 i .NET Framework 4.6.1, jeśli to pomaga zawęzić wszystko. –

Powiązane problemy