Bir özellik veya dizin oluşturucu, çıkış veya ref parametresi olarak iletilemez


90

Yukarıdaki hatayı alıyorum ve çözemiyorum. Biraz Google'da araştırdım ama ondan kurtulamıyorum.

Senaryo:

Mülkü çift tipte olan bütçe olan sınıf BudgetAllocate'im var.

DataAccessLayer'ımda,

Derslerimden birinde bunu yapmaya çalışıyorum:

double.TryParse(objReader[i].ToString(), out bd.Budget);

Bu hatayı atan şey:

Özellik veya dizinleyici, derleme zamanında çıkış veya ref parametresi olarak iletilemez.

Bunu bile denedim:

double.TryParse(objReader[i].ToString().Equals(DBNull.Value) ? "" : objReader[i].ToString(), out bd.Budget);

Geri kalan her şey iyi çalışıyor ve katmanlar arasında referanslar mevcut.


Bd.Budget'ta bd, BudgetAllocate sınıfının bir nesnesidir. Üzgünüm unuttum.
Pratik




Bunu, tanımlanmış alanlara sahip bir kullanıcı türü ile çalışmayı keşfettim DataGrid, sonra doldurmamı beklediğimden sonra onu yalnızca özelliklere sahip otomatikleri öğrenmeye geldim. Özelliklere geçmek, alanlarımda kullandığım bazı ref parametrelerini bozdu. Ayrıştırmanın yapılacağı yerel değişkenler tanımlanmalıdır.
jxramos

Yanıtlar:


39

kullanamazsın

double.TryParse(objReader[i].ToString(), out bd.Budget); 

bd.Budget'ı bazı değişkenlerle değiştirin.

double k;
double.TryParse(objReader[i].ToString(), out k); 

12
neden fazladan bir değişken kullanmalı?
Pratik

7
@pratik Bir özelliği bir çıkış parametresi olarak geçiremezsiniz çünkü özelliğin aslında bir ayarlayıcıya sahip olduğunun garantisi yoktur, bu nedenle ekstra değişkene ihtiyacınız vardır.
matt

25
@ mjd79: Sebebiniz yanlış. Derleyici, ayarlayıcı olup olmadığını bilir. Bir pasör olduğunu varsayalım; izin verilmeli mi?
Eric Lippert

21
@dhinesh, bence OP neden yapamayacağına dair bir cevap arıyor , bunun yerine ne yapması gerektiğine değil. Hans Passant'ın cevabını ve Eric Lippert'in yorumlarını okuyun.
slugster

2
@dhinesh Bunu yapamamasının "gerçek" nedeni, buna izin VEREN VB yerine C # kullanmasıdır. Ben VB dünyasındanım (tabii ki?) Ve C # 'ın getirdiği ekstra kısıtlamalara sık sık şaşırıyorum.
SteveCinq

152

Başkaları size çözümü verdiler, ancak bunun neden gerekli olduğuna gelince: bir özellik, bir yöntem için sadece sözdizimsel şekerdir .

Örneğin, Namebir alıcı ve ayarlayıcı ile çağrılan bir özelliği bildirdiğinizde , başlık altında derleyici aslında get_Name()ve adlı yöntemleri üretir set_Name(value). Ardından, bu özellikten okuduğunuzda ve bu özelliğe yazdığınızda, derleyici bu işlemleri üretilen yöntemlere yapılan çağrılara çevirir.

Bunu göz önünde bulundurduğunuzda, bir özelliği neden bir çıktı parametresi olarak iletemeyeceğiniz açık hale gelir - aslında bir nesneye bir değişkene başvurmak yerine bir yönteme başvuruda bulunuyorsunuz , bu da bir çıktı parametresinin beklediği şeydir.

Dizin oluşturucular için de benzer bir durum söz konusudur.


19
Muhakemeniz son parçasına kadar doğrudur. Out parametresi, bir nesneye değil, bir değişkene başvuru bekliyor .
Eric Lippert

@EricLippert ama bir değişken aynı zamanda bir nesne değil veya neyi kaçırıyorum?
meJustAndrew

6
@meJustAndrew: Bir değişken kesinlikle bir nesne değildir . Değişken, bir depolama yeridir . Bir depolama konumu , (1) referans türündeki (veya boş) bir nesneye bir başvuru veya (2) değer türündeki bir nesnenin değerini içerir. Kabı, içerdiği şeyle karıştırmayın.
Eric Lippert

6
@meJustAndrew: Bir nesne düşünün, mesela bir ev. Üzerinde evin adresi yazan bir kağıt parçası düşünün. O kağıdı içeren bir çekmece düşünün. Ne çekmece ne de kağıt ev değildir .
Eric Lippert

69

Bu, sızdıran bir soyutlama durumudur. Bir özellik, aslında bir yöntemdir olsun ve set gösterilen indekse için erişimcileri get_Index () ve set_Index yöntemlerine derlenmiş olsun. Derleyici, bu gerçeği gizleyerek harika bir iş çıkarır, örneğin bir özelliğe atamayı otomatik olarak karşılık gelen set_Xxx () yöntemine çevirir.

Ancak, bir yöntem parametresini referans olarak ilettiğinizde bu daha da artar. Bu, JIT derleyicisinin geçirilen bağımsız değişkenin bellek konumuna bir işaretçi iletmesini gerektirir. Sorun şu ki, bir özelliğin değerini atamak, ayarlayıcı yöntemini çağırmayı gerektirir. Çağrılan yöntem, geçirilen değişken ile geçirilen özellik arasındaki farkı söyleyemez ve bu nedenle bir yöntem çağrısının gerekli olup olmadığını bilemez.

Dikkat çekici olan, bunun aslında VB.NET'te çalışmasıdır. Örneğin:

Class Example
    Public Property Prop As Integer

    Public Sub Test(ByRef arg As Integer)
        arg = 42
    End Sub

    Public Sub Run()
        Test(Prop)   '' No problem
    End Sub
End Class

VB.NET derleyicisi bunu, C # ile ifade edilen Run yöntemi için bu kodu otomatik olarak oluşturarak çözer:

int temp = Prop;
Test(ref temp);
Prop = temp;

Ayrıca kullanabileceğiniz geçici çözüm budur. C # ekibinin neden aynı yaklaşımı kullanmadığından tam olarak emin değilim. Muhtemelen, potansiyel olarak pahalı alıcı ve ayarlayıcı çağrılarını gizlemek istemedikleri için. Ya da ayarlayıcı, özellik değerini değiştiren yan etkilere sahip olduğunda elde edeceğiniz tamamen teşhis edilemez davranış, atamadan sonra kaybolurlar. C # ve VB.NET arasındaki klasik fark, C # "sürpriz yok", VB.NET "yapabiliyorsanız çalışmasını sağlayın".


16
Pahalı aramalar yapmak istemediğiniz konusunda haklısınız. İkincil bir neden, kopya-in-copy-out anlambiliminin referans anlambiliminden farklı anlambilimlere sahip olmasıdır ve ref geçme için incelikle farklı iki anlambilim tutarsız olacaktır. (Bununla birlikte, derlenmiş ifade ağaçlarının maalesef kopya-içeri-kopyalama yaptığı bazı nadir durumlar vardır.)
Eric Lippert

2
Gerçekten ihtiyaç duyulan şey, daha çeşitli parametre geçirme modlarıdır, böylece derleyici uygun olduğunda "içeri kopyala / kopyala" yerine geçebilir, ancak olmadığı durumlarda ciyaklayabilir.
supercat

9

Çıkış parametresini yerel bir değişkene yerleştirin ve ardından değişkeni şu şekilde ayarlayın bd.Budget:

double tempVar = 0.0;

if (double.TryParse(objReader[i].ToString(), out tempVar))
{
    bd.Budget = tempVar;
}

Güncelleme : Doğrudan MSDN'den:

Özellikler değişken değildir ve bu nedenle parametreler olarak iletilemez.


1
@ E.vanderSpoel Neyse ki içeriği kaldırdım, bağlantıyı kaldırdım.
Adam Houldsworth

8

Muhtemelen ilgi çekicidir - kendiniz yazabilirsiniz:

    //double.TryParse(, out bd.Budget);
    bool result = TryParse(s, value => bd.Budget = value);
}

public bool TryParse(string s, Action<double> setValue)
{
    double value;
    var result =  double.TryParse(s, out value);
    if (result) setValue(value);
    return result;
}

5

Bu çok eski bir gönderi, ancak kabul edileni düzeltiyorum çünkü bunu yapmanın bilmediğim daha da uygun bir yolu var.

Buna satır içi bildirim adı verilir ve her zaman mevcut olabilir (ifadelerde olduğu gibi) veya bu tür durumlar için C # 6.0 veya C # 7.0 ile eklenmiş olabilir, emin değilim, ancak yine de bir cazibe gibi çalışıyor:

Bunun başında

double temp;
double.TryParse(objReader[i].ToString(), out temp);
bd.Budget = temp;

bunu kullan:

double.TryParse(objReader[i].ToString(), out double temp);
bd.Budget = temp;

2
Geçersiz giriş durumunda ayrıştırmanın başarılı olup olmadığını kontrol etmek için dönüşü kullanırdım.
MarcelDevG

2

Öyleyse Budget bir mülk, değil mi?

Bunun yerine, önce yerel bir değişkene ayarlayın ve ardından özellik değerini buna ayarlayın.

double t = 0;
double.TryParse(objReader[i].ToString(), out t); 
bd.Budget = t;

Teşekkürler ama neden böyle olduğunu öğrenebilir miyim?
Pratik

0

Genellikle bunu yapmaya çalıştığımda, bunun nedeni mülkümü ayarlamak veya varsayılan değerde bırakmak istememdir. Bu cevap ve dynamictürlerin yardımıyla, onu tek çizgili ve basit tutmak için kolayca bir dizi genişletme yöntemi oluşturabiliriz.

public static dynamic ParseAny(this string text, Type type)
{
     var converter = TypeDescriptor.GetConverter(type);
     if (converter != null && converter.IsValid(text))
          return converter.ConvertFromString(text);
     else
          return Activator.CreateInstance(type);
}

Böyle kullanın;

bd.Budget = objReader[i].ToString().ParseAny(typeof(double));

// Examples
int intTest = "1234".ParseAny(typeof(int)); // Result: 1234
double doubleTest = "12.34".ParseAny(typeof(double)); // Result: 12.34
decimal pass = "12.34".ParseAny(typeof(decimal)); // Result: 12.34
decimal fail = "abc".ParseAny(typeof(decimal)); // Result: 0
string nullStr = null;
decimal failedNull = nullStr.ParseAny(typeof(decimal)); // Result: 0

İsteğe bağlı

Bir yan not olarak, eğer bu bir durumsa, okuyucunun boş istisnalarını önlemek için uzantılardan SQLDataReaderda yararlanabilirsiniz GetSafeString.

public static string GetSafeString(this SqlDataReader reader, int colIndex)
{
     if (!reader.IsDBNull(colIndex))
          return reader.GetString(colIndex);
     return string.Empty;
}

public static string GetSafeString(this SqlDataReader reader, string colName)
{
     int colIndex = reader.GetOrdinal(colName);
     if (!reader.IsDBNull(colIndex))
          return reader.GetString(colIndex);
     return string.Empty;
}

Böyle kullanın;

bd.Budget = objReader.GetSafeString(i).ParseAny(typeof(double));
bd.Budget = objReader.GetSafeString("ColumnName").ParseAny(typeof(double));
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.