プロパティまたはインデクサーをoutまたはrefパラメーターとして渡すことはできません


86

上記のエラーが発生し、解決できません。私は少しグーグルで検索しましたが、それを取り除くことはできません。

シナリオ:

私はクラスBudgetAllocateを持っています。そのプロパティはdouble型のbudgetです。

私のdataAccessLayerでは、

私のクラスの1つで、これを実行しようとしています。

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 2010




ちょうど私がDataGrid入力することが期待されていたフィールドが定義されたユーザータイプでこれが機能していることを発見し、プロパティを持つ自動車のみを学習するようになりました。プロパティに切り替えると、フィールドで使用していたいくつかのrefパラメータが壊れました。解析を行うローカル変数を定義する必要があります。
jxramos 2017

回答:


37

使用できません

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

bd.Budgetをいくつかの変数に置き換えます。

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

11
なぜ1つの余分な変数を使用するのですか?
Pratik 2010

6
@pratikプロパティに実際にセッターがあるという保証がないため、プロパティをoutパラメーターとして渡すことはできません。そのため、追加の変数が必要です。
マット2010

23
@ mjd79:あなたの推論は正しくありません。コンパイラは、セッターがあるかどうかを認識しています。セッターがいたとしましょう。それは許可されるべきですか?
Eric Lippert 2010

21
@ dhinesh、OPは、代わりに何をしなければならないかだけでなく、なぜそれができないのという答えを探していると思います。HansPassantからの回答とEricLippertからのコメントを読んでください。
slugster 2010

2
@dhinesh彼がそれを行うことができない「本当の」理由は、彼がこれを許可するVBではなくC#を使用しているためです。私はVBの世界から来ました(明らかに?)、C#が課す追加の制限にしばしば驚いています。
SteveCinq 2017

149

他の人があなたに解決策を与えましたが、なぜこれが必要なのかについて:プロパティはメソッドの単なる構文糖衣です。

たとえばName、getterとsetterで呼び出されるプロパティを宣言すると、内部でコンパイラは実際にget_Name()andと呼ばれるメソッドを生成しますset_Name(value)。次に、このプロパティの読み取りと書き込みを行うと、コンパイラはこれらの操作を生成されたメソッドの呼び出しに変換します。

これを考慮すると、あなたが出力パラメータとしてプロパティを渡すことができない理由、それは明らかになった-あなたが実際に参照を渡すことになる方法というを参照するよりも、オブジェクト変数どのような出力パラメータを期待され、。

インデクサーにも同様のケースがあります。


19
あなたの推論は最後のビットまで正しいです。outパラメータは、オブジェクトではなく、変数への参照を想定しています
Eric Lippert 2010

@EricLippertですが、変数でもオブジェクトでもありませんか、何が欠けていますか?
meJustAndrew

6
@meJustAndrew:変数は絶対にオブジェクトではありません。変数は保存場所です。保管場所に、(1)参照型(またはnull)のオブジェクトへの参照、または(2)値型のオブジェクトの値のいずれかが含まれます。コンテナとそれに含まれるものを混同しないでください。
エリックリペット

6
@meJustAndrew:オブジェクト、たとえば家について考えてみましょう。家の住所が書かれた一枚の紙を考えてみましょう。その一枚の紙が入っている引き出しを考えてみましょう。 引き出しも紙も家ではありません
エリックリペット

69

これは、リークのある抽象化のケースです。プロパティは、実際の方法で取得およびセットインデクサ用アクセサは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コンパイラは、C#で表されるRunメソッドの次のコードを自動的に生成することでこれを解決します。

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

これは、同様に使用できる回避策です。C#チームが同じアプローチを使用しなかった理由はよくわかりません。おそらく、彼らは潜在的に高価なゲッターとセッターの呼び出しを隠したくなかったからです。または、セッターにプロパティ値を変更する副作用がある場合に発生する完全に診断不可能な動作は、割り当て後に消えます。C#とVB.NETの古典的な違いは、C#は「驚くことではない」、VB.NETは「可能であれば機能させる」ことです。


15
あなたは高価な呼び出しを生成したくないことについて正しいです。2番目の理由は、コピーインコピーアウトセマンティクスが参照セマンティクスとは異なるセマンティクスを持ち、参照の受け渡しに2つの微妙に異なるセマンティクスを持つことは一貫性がないことです。(とは
いえ

2
本当に必要なのは、さまざまなパラメーター受け渡しモードです。これにより、コンパイラーは、必要に応じて「コピーイン/コピーアウト」に置き換えることができますが、そうでない場合はスクォークします。
スーパーキャット2013

9

outパラメータをローカル変数に配置してから、変数をbd.Budget:に設定します。

double tempVar = 0.0;

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

更新:MSDNから直接:

プロパティは変数ではないため、出力パラメータとして渡すことはできません。


1
@ E.vanderSpoel幸い、私はコンテンツを削除し、リンクを削除しました。
AdamHouldsworth19年

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で追加された可能性がありますが、とにかく魅力のように機能します。

これのイネタッド

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

これを使って:

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

2
入力が無効な場合は、returnを使用して解析が成功したかどうかを確認します。
MarcelDevG 2018年

1

だから予算は財産ですよね?

むしろ、最初にそれをローカル変数に設定し、次にプロパティ値をそれに設定します。

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

ありがとう。でも、なぜそうなのかわかりますか?
Pratik 2010

0

通常、これを実行しようとしているのは、プロパティを設定するか、デフォルト値のままにしておきたいためです。この回答dynamicタイプの助けを借りて、文字列拡張メソッドを簡単に作成して、1行でシンプルに保つことができます。

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

オプション

ちなみに、それが可能であればSQLDataReaderGetSafeString拡張機能を利用して、リーダーからのnull例外を回避することもできます。

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.