2012-02-29 16 views
6

Powiązane pytanie: Accessing a Class property without using dot operatorCasting moją klasę do Int64, Double itp

I utworzeniu klasy o nazwie MyDouble wygląda to

class MyDouble 
{ 
    double value; 
    //overloaded operators and methods 
} 

jestem w stanie wykonać wszystkie rodzaje operacji na MyDouble. Przykłady:

MyDouble a = 5.0; 
a += 3.0; 
...etc 

to jednak nadal zgłasza błąd

MyDouble a = 5.0; 
long b = (Int64)a; //error 
long b = (int64)a.value; //works 

Jak mogę zdefiniować ją tak, że operacja jak (Int64)a automatycznie konwertuje do (Int64)a.value? Nie chcę, aby użytkownik kiedykolwiek musiał się martwić o istnienie właściwości value.

+0

Dlaczego używasz zarówno 'long' i' Int64'? Jeden jest aloasem dla drugiego i myślę, że bardziej idiomatyczny sposób polega na użyciu aliasów, jeśli istnieją zamiast nazw typów. – Joey

Odpowiedz

9

Aby ta konwersja zadziałała, potrzebowałbyś explicit conversion na Int64.

to będzie wyglądać:

class MyDouble 
{ 
    double value; 

    public static explicit operator Int64(MyDouble value) 
    { 
     return (Int64)value.value; 
    } 
} 
+0

Idealne, dziękuję. – xbonez

+0

Dlaczego miałoby to być wyraźne? Konwersja niejawna również działa, a obsada staje się opcjonalna. – Jay

+0

@Jay * Możesz * wykonać operację niejawną, ale w PO pokazała się operator konwersji. Osobiście staram się unikać niejawnych konwersji, chyba że mają sens (domyślnie konwersja z podwójnej na długą wydaje się złym pomysłem). –

1

Istnieje wyraźna konwersja z double do Int64, ale nie ze swojej klasy Int64. wszystko, co musisz zrobić, to zdefiniować jeden. Lub, jeśli masz konwersję z klasy na podwójną, co, jak przypuszczam, możesz zrobić:

długa x = (długa) (podwójna) a;

Jednak jest to uciążliwe; Zdefiniuję operatora konwersji jawnej.

Konwersja powinna być wyraźna, a nie domniemana, chyba że masz dobry powód, aby ją jasno wyrazić.

Konwersja języka z podwójnej na długą jest jawna , ponieważ konwersja może spowodować utratę informacji. Możesz utracić informacje związane z tą konwersją, ponieważ istnieją pewne liczby podwójne, takie jak 0,5, , które nie są konwertowane dokładnie na długie. Nazywa się to przekształcaniem zwężającym .

W przypadku konwersji według języka zawężanie konwersji jest jawne. Gdyby były niejawne, programiści mogliby przypadkowo utracić informacje, przypisując wartość jednego typu do zmiennej innego typu. Zdefiniowanie tej konwersji jako niejawnej narusza tę całkowicie rozsądną zasadę, więc musiałoby to być przekonujące.

Oto przykład pokazujący wartość tej zasady. Jeśli zawężenie konwersji było niejawne, spowodowałoby to, że kod byłby bardziej kruchy. Załóżmy, że konwersja podwójna na int jest domyślna:

class Family 
{ 
    ICollection<Person> Children { get; set; } 
} 

int GetAverageNumberOfChildren(IEnumerable<Family> families) 
{ 
    return families.Sum(f => f.Children.Count)/families.Count(); 
} 

void SomeMethod(IEnumerable<Family> families) 
{ 
    int averageNumberOfChildren = GetAverageNumberOfChildren(families); 
    //... 
} 

Ups! Mamy błąd! GetAverageNumberOfChildren powinien zwrócić double! Naprawmy to!

double GetAverageNumberOfChildren(IEnumerable<Family> families) 
{ 
    return families.Average(f => f.Children.Count); 
} 

Uff, wszystko się kompiluje i działa! Na szczęście cieliśmy się tym błędem!

Ale, niestety, nie. Nadal mamy błąd!SomeMethod niejawnie konwertuje wartość podwójną na int, więc jeśli oczekiwana wartość to 2.4, faktyczna wartość to 2.

Ponieważ konwersja podwójna na int jest jawna, kompilator ratuje nas przed nami. Po naprawieniu błędu GetAverageNumberOfChildren, błędny program nie zostanie skompilowany.

Teraz, jeśli twoja klasa MyDouble miał jakieś walidacji że ograniczony ją do liczby całkowitej wartości z zakresu równego lub mniejszego niż zakres long, wtedy będzie bezpieczna, aby konwersja niejawna. Ale w takim przypadku powinieneś oczywiście używać long, a nie double.

1

Niejawna konwersja zadziała tak samo, jak wyraźna konwersja.

public static implicit operator long(MyDouble m) 
{ 
    return (long) m.value; 
} 

Mając to na miejscu można zrobić:

long b = (Int64) a; 

lub po prostu

long b = a; 

To jest po prostu pokazują, że konwersja nie musi być jednoznaczna. Jak jednak wspomniano, to powinno być jednoznaczne ze względu na możliwość utraty danych.

+0

To prawda, ale niebezpieczne. Zobacz moją zredagowaną odpowiedź, aby uzyskać wyjaśnienie. – phoog

+0

@phoog Rozumiem. Miałem problem tylko z językiem "ty * potrzebujesz * wyraźnej konwersji". – Jay

+0

Wystarczająco sprawiedliwe; Po prostu pomyślałem, że jeśli omawiamy istnienie niejawnych konwersji, dobrze byłoby przedyskutować powody wyraźnego zawężenia konwersji. W przeciwnym razie ktoś mógłby ulec pokusie wdrożenia konwersji, która powinna być jawna jako niejawna konwersja "ponieważ jest łatwiejsza" lub "ponieważ powoduje, że mój kod jest czystszy", nie biorąc pod uwagę zwiększonego prawdopodobieństwa napisania błędnych programów. – phoog