2012-06-21 6 views
11

Geçtiğim ve kodumda kullandığım iki farklı türde dizim var ve ikisi birbiriyle yakından ilişkili, ancak birbiriyle karıştırılmamalıdır. Sadece dizeleri olan iki sınıfa sahip olmaktan ziyade farklı isimlerle hatalardan kaçınmam için yardım edebileceğimi düşündüm, böylece yöntem imzaları (ve genel olarak uyumsuzluğu yazın), iki farklı türdeki dizenin anlamsal anlamını zorlar. Aynı zamanda, yüzlerce yerde something = "foo";'dan something.Value = "foo";'a refactor yapmak istemedim.Türetilmiş "kabuk" sınıflarını, hiçbir şey yapmayan, ancak yeniden adlandırılan türler olarak nasıl bildirebilirim?

İlk düşünce:

private class FirstKind : string { } 
private class SecondKind : string { } 

Ben

void MyMethod(FirstKind varOne, SecondKind varTwo) {...} 

var, sonra MyMethod(secondKindVar, firstKindVar); ile aramayı deneyin eğer, bir derleyici hatası olsun ki varlık fikir.

Duvar I çarptı: string mühürlendi.

İkinci düşünce: hiçbir şey yapmaz, ancak örtük dönüştürme işleçleri ile değeri dışarı çıkarmak ve tükürmek yerine jenerik KindOf<T> sınıfı oluştur. Şunun gibi:

private class KindOf<T> 
{ 
    public T Value { get; set; } 
    public KindOf() { Value = default(T); } 
    public KindOf(T val) { Value = val; } 
    public static implicit operator T(KindOf<T> kindOf) { return kindOf.Value; } 
    public static implicit operator KindOf<T>(T value) { return new KindOf<T>(value); } 
} 

Böyle bir şey yapabileceğini Bu şekilde: ben vurmak

private class FirstKind : KindOf<string> { } 
private class SecondKind : KindOf<string> { } 

Duvar: anlam, kurucular ne de operatörler ne türetilmiş türlerinde mevcuttur: KindOf<T> sınıftan hiçbir şey miras gibi görünüyor Temel olarak bütün sınıfları türetilmiş sınıflarda yeniden uygulamak zorundayım. Kod kopyalama. Yuck.

Bu yüzden burada temel bir şeyi kaçırıyormuşum gibi hissediyorum, belki de yabani otların arasındayım. Ne yapmaya çalıştığım hakkında bir fikrin var mı?

+1

Belki "iki farklı çeşit dizinin" ne anlama geldiğini açıklayabilir misiniz? –

+1

Başımın üst kısmından, alt sınıflardaki örtük operatörleri ve kurucuları uygulamanız gerekebilir. Evet, kod kopyalama gibi bir şey, ama en azından devralma işlemi veya miras gerçekten hiçbir şey (retorik olarak, bir "KindOf" nedir ve gerçekten bir şey ifade ediyor? Orada benzer sözdizimi (ancak farklı kullanımlar) yanında ortak hiçbir şeyi olmayan sınıflar arasında kod çoğaltmayı azaltmak için ( –

+0

Anlaşılan), sınıflar gerçekten de aynı kalıtım hiyerarşisinde olmamalıdır, özellikle * istemiyorsanız * onlara atıfta bulunarak Ortak bir tiple, onların ayrı kalmasını istersiniz. – Servy

cevap

0

Bunu sadece dizeler için yapardım.

abstract class MyTypedString 
{ 
    protected MyTypedString(string value) 
    { 
    Value = value; 
    } 
    public string Value { get; private set; } 
} 

Sonra FirstKind ve SecondKind senin bu soyut sınıf bitirmek. Bu, nesnenin nasıl olduğu gibi nesneyi değişmez tutacaktır. Bunu herhangi bir gizli dönüşüm operatörü vermeyeceğim.

+0

Bu aslında onun ikinci örneği ile aynı (sadece daha az güzel). –

0

Operatörler hala sadece kindof sınıfın .Value üyesi başvurmak gerekir çalışacaktır: Ancak dediğin gibi

FirstKind stringFoo = new FirstKind("foo"); 
FirstKind stringQux = new FirstKind("qux"); 

// String concatenation operator 
FirstKind stringTar = new FirstKind(stringFoo.Value + stringQux.Value); 

, orada olacağım hayır "tip denetimi" Eğer iki karışımı halinde, örneğin

SecondKind stringBar = new SecondKind("bar"); 
FirstKind stringHal = new FirstKind(stringFoo.Value + stringBar.Value); // this will succeed, even though you intend stringFoo and stringBar to be incompatible 

operatörler sınıfın parçası olduğundan arayüzü sonra onları yeniden uygulamalıdır - Sadece içerdiği sınıf sarabilirsiniz ancak uygulaması, sadece işe bu almak için sıkıcı hırıltı-çalışma yapmak gerekir, böylece .

0

Sınıfınızdaki implicit dönüşüm işlecini bir dizeye aşırı yüklemeyi deneyebilirsiniz. Sonra referanslarınızı güncelleştirmek zorunda kalmazsınız. Sınıflar ayrı kalmalı ve onlar için dize değerini kabul eden kurucular oluşturmalısınız. En iyi uygulama, readonly dizgesini tutmak, böylece sınıfın değişmez hale gelmesidir. String sınıfını taklit etmek, onun bir temsilidir.

public class FirstKind 
{ 
     private readonly string value; 
     public FirstKind(string v) { this.value = v }; 
     public static implicit operator string(FirstKind v){ return v.value; } 
} 

public class SecondKind 
{ 
     private readonly string value; 
     public SecondKind (string v) { this.value = v }; 
     public static implicit operator string(SecondKind v){ return v.value; } 
} 
+1

Zaten yapmadan önce ... – Servy

+0

Yanlışlıkla gönderilmiş. – Fr33dan

+0

O zaten bunu yapıyor ... – Servy

0

Genel sınıfınızı neredeyse kullanabilirsiniz. Dize (veya başka bir türden) derlenmiş sınıfa yalnızca örtülü döküm işlemi hiçbir zaman çalışmaz, çünkü temel sınıf dize ilk kuşak veya ikinci türe atılacaksa, bunu bilemez. o kısmı kopyalanamaz edilmelidir Yani, gerisi ancak temel sınıfta kalabilirler:

private class FirstKind : KindOf<string> 
{ 
    public static implicit operator FirstKind(string value) { return new FirstKind{Value = value}; } 
} 
private class SecondKind : KindOf<string> 
{ 
    public static implicit operator SecondKind(string value) { return new SecondKind{Value = value}; } 
} 

hala jenerik sınıfına yapılabilir temel sınıf için Casting:

 FirstKind Foo = "Foo"; //via implicit operator in FirstKind 
     string stringFoo = Foo; //via implicit operator in baseclass 
+0

Kalan sorun, yine de Foo.Length' gibi bir şey yapamazsınız. Muhtemelen tüm zorluklara değmez "Foo.Value.Length" gibi şeyler yapmak için tüm kodunuzu düzeltmeniz gerekir. –

+0

Evet, buna karşılık KindOf 'dan gelen KindOfString sınıfı için bir çağrı ve diğer türlerin devraldığı (veya yalnızca dizeler kullanılmışsa, genel sınıfı tamamen atla) bu özellikleri uygular. –

3

Dürüst olmak gerekirse, Gizli operatörleri ya da bu konudaki herhangi bir kalıtımı kullanmazdım. Bütün fikir, bir FirstKind alan bir yönteme sahip olmaktır. Kapalı işleç ile bir dizgeyi geçirebilir ve dönüştürülecektir; Bu, kodu ilk/ikinci tür olduğunu açıkça belirtmek için zorlamadığınız anlamına gelir. Her biri sadece bir kurucu ve bir salt okunur özelliği olan bir kurucuya sahip iki sınıfla yapışırdım. Başka bir şey ekleyerek onu zorlaştırıyorsun.

can sıkıcı elde ettiğini özelliğini kullanarak, muhtemelen özel sınıfların her birinden, örtük dönüştürme - dize görebiliyordu ama dizesinden zarar verecek bir örtük dönüştürme inanıyoruz yoksa . Kopyala/yapıştır koduna katılıyorum genellikle koddan kaçınmakla birlikte, tam anlamıyla tek bir kod satırı olduğunda, her iki sınıfa örtülü dönüşümün kopyalanması daha kolay, daha etkili ve genel olarak miras veya başka bir şey kullanmaya çalışmaktan daha kolay olacaktır. iki kere yazmamak için.

+0

+1 Ne yazık ki, 'String' mühürlendiğinden, korkarım bu muhtemelen en iyi seçenek. –

+0

Buna katılıyorum. "Dize örtük dönüştürme" benim arkamdaki fikirdi, ama dersleri önerdiğin gibi ayrı tutuyor. – Fr33dan

1

Benim yorumumu bir cevap olarak yapacağım çünkü bence tek olarak duruyor.

Başımın üst kısmından, alt sınıflardaki örtük operatörleri ve kurucuları uygulamanız gerekebilir. Evet, kod kopyalama gibi bir şey, ama en azından devrik bir süreç kullanmıyor ya da miras gerçekten gerçek bir şey (retorik olarak, bir "KindOf" neyse gerçekten bir şey ifade etmiyorsa ve gerçekten bir şey ifade etmiyorsa) miras kullanarak değil mi? Eğer onlar dizeleri temsil zannediyorsunuz eğer sınıf iletmenin yapma düşünebilirsiniz,

public class FirstKind 
{ 
    public string Value { get; private set; } 

    public FirstKind(string value) 
    { 
     this.Value = value; 
    } 

    public static implicit operator FirstKind(string value) 
    { 
     return new FirstKind(value); 
    } 

    public static implicit operator string(FirstKind value) 
    { 
     return value.Value; 
    } 
} 

Ayrıca benzer sözdizimi (ancak farklı kullanımlar) yanında ortak bir yönü yok sınıflar arasında kod tekrarını orada azaltmak için.

Çok fazla kablolama değil ve (gelecekte değişiklikler için açık kodunuzu bırakır (işaret olabilir başka uygulama detay belki bu nesneler, uygulamanızda, her zaman onları değişken kılan bir yeniden KindOf sınıftan miras olmamalıdır kadar) belki yeni bir özellik eklemek ister misiniz? Bazı doğrulama kodu ekle?) ve API tüketicileri gerçekten KindOf sadece bazı küçük kod çoğaltma kaydetmek için umurumda değil. Sorunuza olarak

1

, FirstKind (belki? hayatınızı biraz daha kolay hale getirmek için bir pasajı yapabilirsiniz) ve SecondKind dize benzeri değişkenlerin tipleri vardır ve string senin KindOf<T> sınıfın tipi argümandır.

public class FirstKind { } 
public class SecondKind { } 

public struct String<TKind> 
{ 
    private readonly string _value; 

    public String(string value) 
    { 
     _value = value; 
    } 

    public override string ToString() 
    { 
     return _value; 
    } 

    public static implicit operator String<TKind>(string s) // see the note below 
    { 
     return new String<TKind>(s); 
    } 

    public static implicit operator string(String<TKind> s) 
    { 
     return s.ToString(); 
    } 

    public static String<TKind> operator +(String<TKind> s1, String<TKind> s2) 
    { 
     return new String<TKind>(s1.ToString() + s2.ToString()); 
    } 

    public int Length 
    { 
     get { return _value.Length; } 
    } 

    // You can add methods like Substring and Trim that delegate to _value. 
} 

: (diğer cevaplar gerektirdiği kod tekrarını önler) farklı bir yaklaşım onları String<TKind> türünün tür argümanlar olarak kullanılan "işaretleyici" sınıfları boş hale FirstKind ve SecondKind rolünü çevirmek etmektir Şimdi böyle bir MyMethod ilan edebilir:

void MyMethod(String<FirstKind> varOne, String<SecondKind> varTwo) 
{ 
    varOne += varOne; // OK 
    varTwo += varTwo; // OK 
    varOne = varTwo; // ERROR 
} 

Not: bir düz string gelen örtük dönüştürme operatörü bıraktım. Bunu kaldırarak, dize türünü açıkça belirtmek için kodu zorlamanız gerekebilir: new String<FirstKind>("...").

+0

Hm, ilginç bir yaklaşım. Bunu düşünmek zorundayım. – Atario

İlgili konular