ไม่สามารถส่งคุณสมบัติหรือตัวสร้างดัชนีเป็นพารามิเตอร์ out หรือ ref ได้


90

ฉันได้รับข้อผิดพลาดข้างต้นและไม่สามารถแก้ไขได้ ฉัน googled เล็กน้อย แต่ไม่สามารถกำจัดมันได้

สถานการณ์:

ฉันมีคลาส BudgetAllocate ที่มีคุณสมบัติเป็นงบประมาณซึ่งเป็นประเภทสองเท่า

ใน dataAccessLayer ของฉัน

ในชั้นเรียนของฉันฉันพยายามทำสิ่งนี้:

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

ซึ่งทำให้เกิดข้อผิดพลาดนี้:

คุณสมบัติหรือตัวสร้างดัชนีไม่สามารถส่งผ่านเป็นพารามิเตอร์ out หรือ ref ได้ในเวลาคอมไพล์

ฉันยังลองสิ่งนี้:

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

อย่างอื่นทำงานได้ดีและมีการอ้างอิงระหว่างเลเยอร์


ใน bd.Budget bd เป็นวัตถุของคลาส BudgetAllocate ขอโทษฉันลืม.
Pratik




เพิ่งค้นพบสิ่งนี้ใช้งานได้กับประเภทผู้ใช้ที่มีการกำหนดฟิลด์ซึ่งฉันคาดว่าDataGridจะเติมข้อมูลจากนั้นมาเรียนรู้เฉพาะรถยนต์ที่มีคุณสมบัติ การเปลี่ยนไปใช้คุณสมบัติทำให้พารามิเตอร์อ้างอิงบางอย่างที่ฉันใช้ในฟิลด์ของฉันเสียหาย ต้องกำหนดตัวแปรภายในเพื่อทำการแยกวิเคราะห์ด้วย
jxramos

คำตอบ:


39

คุณไม่สามารถใช้

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

แทนที่ bd.Budget ด้วยตัวแปรบางตัว

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

12
ทำไมต้องใช้ตัวแปรพิเศษตัวเดียว ??
Pratik

7
@pratik คุณไม่สามารถส่งผ่านคุณสมบัติเป็นพารามิเตอร์ out ได้เนื่องจากไม่มีการรับประกันว่าคุณสมบัตินั้นมีตัวตั้งค่าอยู่ดังนั้นคุณจึงต้องใช้ตัวแปรพิเศษ
แมต

25
@ mjd79: เหตุผลของคุณไม่ถูกต้อง คอมไพเลอร์รู้ว่ามีตัวเซ็ตหรือไม่ สมมติว่ามี setter; ควรได้รับอนุญาตไหม
Eric Lippert

21
@dhinesh ฉันคิดว่า OP กำลังหาคำตอบว่าทำไมเขาถึงทำไม่ได้ไม่ใช่แค่สิ่งที่เขาต้องทำแทน อ่านคำตอบจาก Hans Passant และความคิดเห็นจาก Eric Lippert
slugster

2
@dhinesh เหตุผลที่ "จริง" ที่เขาทำไม่ได้เป็นเพราะเขาใช้ C # มากกว่า VB ซึ่งอนุญาตให้ทำเช่นนี้ ฉันมาจากโลก VB (ชัดมั้ย?) และฉันมักจะประหลาดใจกับข้อ จำกัด พิเศษที่ C # กำหนด
SteveCinq

152

อื่น ๆ ได้ให้คุณแก้ปัญหา แต่เป็นไปทำไมนี้เป็นสิ่งจำเป็น: คุณสมบัติเป็นเพียงน้ำตาลประโยคหาวิธีการ

ตัวอย่างเช่นเมื่อคุณประกาศคุณสมบัติที่เรียกว่าNameมีความทะเยอทะยานและหมาภายใต้ฝากระโปรงคอมไพเลอร์จริงสร้างวิธีการที่เรียกว่าและget_Name() set_Name(value)จากนั้นเมื่อคุณอ่านและเขียนถึงคุณสมบัตินี้คอมไพลเลอร์จะแปลการดำเนินการเหล่านี้เป็นการเรียกใช้เมธอดที่สร้างขึ้น

เมื่อคุณพิจารณาเรื่องนี้มันจะกลายเป็นที่ชัดเจนว่าทำไมคุณไม่สามารถผ่านคุณสมบัติเป็นพารามิเตอร์การส่งออก - คุณจะจริงจะผ่านการอ้างอิงถึงวิธีการมากกว่าการอ้างอิงไปยังวัตถุตัวแปรซึ่งเป็นสิ่งที่คาดหวังกับตัวแปรเอาท์พุท

มีกรณีที่คล้ายกันสำหรับตัวทำดัชนี


19
เหตุผลของคุณถูกต้องจนถึงบิตสุดท้าย พารามิเตอร์ออกคาดว่าการอ้างอิงถึงตัวแปรไม่ไปยังวัตถุ
Eric Lippert

@EricLippert แต่ตัวแปรไม่ใช่วัตถุหรือฉันขาดอะไรไป?
meJustAndrew

6
@meJustAndrew: ตัวแปรอย่างไม่ได้เป็นวัตถุ ตัวแปรเป็นสถานที่เก็บ ตำแหน่งที่จัดเก็บประกอบด้วย (1) การอ้างอิงถึงอ็อบเจ็กต์ประเภทการอ้างอิง (หรือ null) หรือ (2) ค่าของอ็อบเจ็กต์ประเภทค่า อย่าสับสนระหว่างภาชนะสำหรับสิ่งที่บรรจุอยู่
Eric Lippert

6
@meJustAndrew: พิจารณาวัตถุพูดบ้าน พิจารณาแผ่นกระดาษที่เขียนที่อยู่ของบ้านไว้ พิจารณาลิ้นชักที่มีกระดาษแผ่นนั้น ทั้งลิ้นชักหรือกระดาษที่มีบ้าน
Eric Lippert

69

นี่เป็นกรณีของนามธรรมที่รั่วไหล คุณสมบัติคือเมธอดgetและset accessors สำหรับ indexer จะคอมไพล์เป็นเมธอด get_Index () และ set_Index คอมไพเลอร์ทำงานได้อย่างยอดเยี่ยมโดยซ่อนความจริงนั้นโดยจะแปลการมอบหมายงานเป็นคุณสมบัติเป็นเมธอด set_Xxx () ที่เกี่ยวข้องโดยอัตโนมัติ

แต่สิ่งนี้จะเกิดขึ้นเมื่อคุณส่งพารามิเตอร์วิธีการโดยการอ้างอิง ที่ต้องการให้คอมไพเลอร์ JIT ส่งตัวชี้ไปยังตำแหน่งหน่วยความจำของอาร์กิวเมนต์ที่ส่งผ่าน ปัญหาคือไม่มีอย่างใดอย่างหนึ่งการกำหนดค่าของคุณสมบัติจำเป็นต้องเรียกเมธอด setter วิธีการที่เรียกว่าไม่สามารถบอกความแตกต่างระหว่างตัวแปรที่ส่งผ่านและคุณสมบัติที่ส่งผ่านและไม่สามารถทราบได้ว่าจำเป็นต้องมีการเรียกใช้เมธอดหรือไม่

ที่น่าสังเกตคือสิ่งนี้ใช้งานได้จริงใน VB.NET ตัวอย่างเช่น:

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 แก้ปัญหานี้โดยการสร้างรหัสนี้โดยอัตโนมัติสำหรับเมธอด Run ซึ่งแสดงใน C #:

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

ซึ่งเป็นวิธีแก้ปัญหาที่คุณสามารถใช้ได้เช่นกัน ไม่ค่อยแน่ใจว่าทำไมทีม C # ถึงไม่ใช้แนวทางเดียวกัน อาจเป็นเพราะพวกเขาไม่ต้องการซ่อนสายเรียกเข้าและ setter ที่อาจมีราคาแพง หรือพฤติกรรมที่ไม่สามารถวินิจฉัยได้อย่างสมบูรณ์ที่คุณจะได้รับเมื่อตัวตั้งค่ามีผลข้างเคียงที่เปลี่ยนแปลงมูลค่าคุณสมบัติสิ่งเหล่านี้จะหายไปหลังจากการมอบหมาย ความแตกต่างแบบคลาสสิกระหว่าง C # และ VB.NET, C # คือ "ไม่น่าแปลกใจ" VB.NET คือ "ทำให้มันใช้งานได้ถ้าคุณทำได้"


16
คุณถูกต้องเกี่ยวกับการไม่ต้องการสร้างการโทรที่มีราคาแพง เหตุผลรองคือความหมายแบบ copy-in-copy-out มีความหมายที่แตกต่างจากความหมายอ้างอิงและจะไม่สอดคล้องกันที่จะมีความหมายที่แตกต่างกันอย่างละเอียดสองแบบสำหรับการส่งผ่านการอ้างอิง (ที่กล่าวว่ามีบางสถานการณ์ที่หายากที่แผนภูมินิพจน์ที่รวบรวมจะคัดลอกในคัดลอกออก)
Eric Lippert

2
สิ่งที่จำเป็นจริงๆคือโหมดการส่งผ่านพารามิเตอร์ที่หลากหลายมากขึ้นเพื่อให้คอมไพเลอร์สามารถแทนที่ "copy in / copy out" ได้ตามความเหมาะสม แต่ในกรณีที่ไม่เป็นเช่นนั้น
supercat

9

วางพารามิเตอร์ out ลงในตัวแปรโลคัลจากนั้นตั้งค่าตัวแปรเป็นbd.Budget:

double tempVar = 0.0;

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

อัปเดต : ตรงจาก MSDN:

คุณสมบัติไม่ใช่ตัวแปรดังนั้นจึงไม่สามารถส่งผ่านเป็นพารามิเตอร์ออกได้


1
@ E.vanderSpoel โชคดีที่ฉันยกเนื้อหาออกลบลิงค์ออก
Adam Houldsworth

8

อาจเป็นที่สนใจ - คุณสามารถเขียนของคุณเอง:

    //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

นี่เป็นโพสต์เก่ามาก แต่ฉันกำลังแก้ไขให้เป็นที่ยอมรับเพราะมีวิธีที่สะดวกสบายมากขึ้นในการทำสิ่งนี้ซึ่งฉันไม่รู้

เรียกว่าการประกาศแบบอินไลน์และอาจมีอยู่ตลอดเวลา (เช่นเดียวกับการใช้คำสั่ง) หรืออาจมีการเพิ่ม C # 6.0 หรือ C # 7.0 สำหรับกรณีดังกล่าวไม่แน่ใจ แต่ก็ใช้งานได้ดีเช่นกัน:

Inetad ของสิ่งนี้

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

ใช้สิ่งนี้:

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

2
ฉันจะใช้การส่งคืนเพื่อตรวจสอบว่าการแยกวิเคราะห์สำเร็จหรือไม่ในกรณีที่ป้อนข้อมูลไม่ถูกต้อง
MarcelDevG

2

ดังนั้นงบประมาณจึงเป็นทรัพย์สินใช่หรือไม่?

ก่อนอื่นให้ตั้งค่าเป็นตัวแปรท้องถิ่นจากนั้นตั้งค่าคุณสมบัติเป็นค่านั้น

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

ขอบคุณ แต่ฉันจะรู้ได้อย่างไร
Pratik

0

โดยปกติเมื่อฉันพยายามทำสิ่งนี้เป็นเพราะฉันต้องการตั้งค่าคุณสมบัติของฉันหรือปล่อยให้เป็นค่าเริ่มต้น ด้วยความช่วยเหลือของคำตอบและdynamicประเภทนี้เราสามารถสร้างวิธีการขยายสตริงได้อย่างง่ายดายเพื่อให้บรรทัดเดียวและเรียบง่าย

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);
}

ใช้แบบนั้น;

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

ไม่จำเป็น

หมายเหตุด้านข้างหากเป็นเช่นนั้นSQLDataReaderคุณอาจใช้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;
}

ใช้แบบนั้น;

bd.Budget = objReader.GetSafeString(i).ParseAny(typeof(double));
bd.Budget = objReader.GetSafeString("ColumnName").ParseAny(typeof(double));
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.