Neden bu dize, uzatma yöntemi bir istisna değildir?
Bir dize içinde bir alt dize tüm dizinler IEnumerable<int>
döndürmesi için bir C var# string uzantısı yöntemi. Bu mükemmel çalışıyor kullanım amacı ve beklenen sonuçları döndürülür (olarak kanıtlanmış tek yaptığım testler olmamakla birlikte aşağıda), ama başka bir birim test keşfetti bir sorun: bunu yapamazsın boş argümanlar.
İşte uzatma yöntemi test ediyorum:
public static IEnumerable<int> AllIndexesOf(this string str, string searchText)
{
if (searchText == null)
{
throw new ArgumentNullException("searchText");
}
for (int index = 0; ; index = searchText.Length)
{
index = str.IndexOf(searchText, index);
if (index == -1)
break;
yield return index;
}
}
Burada sorun işaretlenmiş test:
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void Extensions_AllIndexesOf_HandlesNullArguments()
{
string test = "a.b.c.d.e";
test.AllIndexesOf(null);
}
Test uzatma yöntemim karşı çalıştığında, başarısız olur, bu yöntem standart bir hata mesajı ile "istisna değil".
Açıkça işlev null
geçtim, nedense karşılaştırma null == null
false
dönüyor yine. bu kafa karıştırıcı: Bu nedenle, bir istisna atılır ve kod devam ediyor.
Ben teyit bu hata ile testi: ne zaman çalışan bir yöntem benim ana proje ile arama Console.WriteLine
null-karşılaştırma if
blok, bir şey gösterilen konsol ve bir istisna değildir tarafından yakalanan herhangi bir catch
blok ekliyorum. Ayrıca, == null
yerine string.IsNullOrEmpty
kullanarak aynı sorun var.
Neden bu sözüm ona basit bir karşılaştırma başarısız mı?
CEVAP
yield return
kullanıyorsunuz. Bunu yaparken, derleyici devlet bir makine uygulayan oluşturulan bir sınıf döndüren bir işlev içine yönteminizi yeniden.
Genel olarak konuşursak, bu sınıfın alanlar için yerli yazar ve yield return
Talimatlar arasında algoritması her bir parçası bir devlet haline gelir. Bu yöntem, derleme sonra ne olacağını bir önizleme (yield return
yetiştirip, akıllı yazılım kapatmak için emin olun) ile kontrol edebilirsiniz.
Ama alt satırında:yöntem kod yineleme başlayana kadar idam olmayacak.
Ön koşulları kontrol etmek için her zamanki gibi iki yöntem ayrı ayrı
public static IEnumerable<int> AllIndexesOf(this string str, string searchText)
{
if (str == null)
throw new ArgumentNullException("str");
if (searchText == null)
throw new ArgumentNullException("searchText");
return AllIndexesOfCore(str, searchText);
}
private static IEnumerable<int> AllIndexesOfCore(string str, string searchText)
{
for (int index = 0; ; index = searchText.Length)
{
index = str.IndexOf(searchText, index);
if (index == -1)
break;
yield return index;
}
}
Bu ilk yöntem beklediğiniz gibi davranır çünkü işleri (acil çalıştırma) ve devlet Makine İkinci yöntem tarafından hayata döndürür.
Ayrıca uzantıları yöntem çünkü null
str
parametre kontrol etmeniz gerektiğini unutmayınolabilirnull
değerleri üzerine denir, sadece sözdizimsel şeker.
Eğer derleyici kodunuzu yaptıklarını merak ediyorsanız, işte size yöntemi, dotPeek kullanarak ile decompiledHaritayı Derleyici tarafından üretilen Kodseçeneği.
public static IEnumerable<int> AllIndexesOf(this string str, string searchText)
{
Test.<AllIndexesOf>d__0 allIndexesOfD0 = new Test.<AllIndexesOf>d__0(-2);
allIndexesOfD0.<>3__str = str;
allIndexesOfD0.<>3__searchText = searchText;
return (IEnumerable<int>) allIndexesOfD0;
}
[CompilerGenerated]
private sealed class <AllIndexesOf>d__0 : IEnumerable<int>, IEnumerable, IEnumerator<int>, IEnumerator, IDisposable
{
private int <>2__current;
private int <>1__state;
private int <>l__initialThreadId;
public string str;
public string <>3__str;
public string searchText;
public string <>3__searchText;
public int <index>5__1;
int IEnumerator<int>.Current
{
[DebuggerHidden] get
{
return this.<>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden] get
{
return (object) this.<>2__current;
}
}
[DebuggerHidden]
public <AllIndexesOf>d__0(int <>1__state)
{
base..ctor();
this.<>1__state = param0;
this.<>l__initialThreadId = Environment.CurrentManagedThreadId;
}
[DebuggerHidden]
IEnumerator<int> IEnumerable<int>.GetEnumerator()
{
Test.<AllIndexesOf>d__0 allIndexesOfD0;
if (Environment.CurrentManagedThreadId == this.<>l__initialThreadId && this.<>1__state == -2)
{
this.<>1__state = 0;
allIndexesOfD0 = this;
}
else
allIndexesOfD0 = new Test.<AllIndexesOf>d__0(0);
allIndexesOfD0.str = this.<>3__str;
allIndexesOfD0.searchText = this.<>3__searchText;
return (IEnumerator<int>) allIndexesOfD0;
}
[DebuggerHidden]
IEnumerator IEnumerable.GetEnumerator()
{
return (IEnumerator) this.System.Collections.Generic.IEnumerable<System.Int32>.GetEnumerator();
}
bool IEnumerator.MoveNext()
{
switch (this.<>1__state)
{
case 0:
this.<>1__state = -1;
if (this.searchText == null)
throw new ArgumentNullException("searchText");
this.<index>5__1 = 0;
break;
case 1:
this.<>1__state = -1;
this.<index>5__1 = this.searchText.Length;
break;
default:
return false;
}
this.<index>5__1 = this.str.IndexOf(this.searchText, this.<index>5__1);
if (this.<index>5__1 != -1)
{
this.<>2__current = this.<index>5__1;
this.<>1__state = 1;
return true;
}
goto default;
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
void IDisposable.Dispose()
{
}
}
Bu geçersiz bir C# kodu, çünkü derleyici izin verilen şeyleri dile gelmez, ama izin veren yasal IL - mesela adlandırma değişkenleri bir yol olamazdı önlemek için Ad çakışması.
Ama gördüğünüz gibi, sadece bazı devlet başlatır kimin AllIndexesOf
sadece oluşturur ve döndürür bir nesne,. GetEnumerator
tek kopya nesne. Asıl iş sıralanıyor (MoveNext
yöntemini çağırarak) başlattığınızda yapılır.
Neden orada IEnumerable hiçbir dosyala...
Özel durum açıklaması ve yığın bir ist...
Neden dize Kaldırın() yöntemi parametr...
Neden istisna değildir.() printStackTr...
Neden '' anahtar kelime uzat...