6

更新:発生していた問題を修正しましたが、バグがスタック トレースを生成した理由がわかりません。スタック トレースは、完全に間違った方向に私を導きます。誰かがここで何が起こっているのかを説明できれば、私はそれを感謝します (そして、あなたの答えを受け入れたものとしてマークします)。私の元の投稿は削除されていることに注意してください。

次のクラスがありました。関係のない部分は削除されました。

class ClassName {
    private string[] _accountTypes = new string[2] {"ECOM", "MOTO"};

    private Dictionary<string, string> _settleDueDateDictionary = new Dictionary<string, string>() {
        {"0", "Process immediately."},
        {"1", "Wait 1 day"},
        {"2", "Wait 2 days"},
        {"3", "Wait 3 days"},
        {"4", "Wait 4 days"},
        {"5", "Wait 5 days"},
        {"6", "Wait 6 days"},
        {"7", "Wait 7 days"},
    };


    private string _settleDueDate;

    private string _accountTypeDescription;


    public string SettleDueDate
    {
        get
        {
            DateTime today = DateTime.Today;
            long settleDueDate = Convert.ToInt64(_settleDueDate);
            return today.AddDays(settleDueDate).ToString("MM/dd/yyyy");
        }
        set
        {   
            if (!_settleDueDateDictionary.ContainsKey(value)) {
                // TODO - handle
            }
            _settleDueDate = value;
        }
    }

    public string AccountTypeDescription
    {
        get {
            //return AccountTypeDescription; // This would cause infinite recursion (not referring to backing property).
            return _accountTypeDescription; // This fixed the StackOverflowException I was faxed with
        }
        set
        {
            if (!_accountTypes.Contains(value))
            {
                // TODO - handle
            }
            _accountTypeDescription = value;
        }
    }
}

上記のクラスのインスタンスを取得し、インスタンスの値を使用して XML 文字列を作成するこのクラスもありました。

class SecondClass
{
    private ClassName classnameInstance;

    public SecondClass(ClassName instance)
    {
        classnameInstance = instance;
    }

    public string PrepareRequest(XMLWriter writer)
    {
        writer.WriteElementString("accounttypedescription", classnameInstance.AccountTypeDescription);
    }
}

スタック トレースを生成したクライアント コードは次のとおりです。

STPPData STPP = new STPPData();

STPP.SiteReference = _secureTradingWebServicesPaymentSettings.SiteReference;
STPP.Alias = _secureTradingWebServicesPaymentSettings.Alias;

STPP.SettleDueDate = Convert.ToString(_secureTradingWebServicesPaymentSettings.SettleDueDate);
STPP.SettleStatus = _secureTradingWebServicesPaymentSettings.SettleStatus;
STPPXml STPPXml = new STPPXml(STPP);

XmlWriterSettings settings = new XmlWriterSettings();
settings.Async = false;
var builder = new StringBuilder();

using (XmlWriter writer = XmlWriter.Create(builder, settings))
{
    string xmlRequest = STPPXml.PrepareRequest(writer);
}

最後に、スタック トレースを次に示します。

mscorlib.dll!string.GetHashCode()
mscorlib.dll!System.Collections.Generic.GenericEqualityComparer<System.__Canon>.GetHashCode(SYstem.__Canon obj)
mscorlib.dll!System.Collections.Generic.Dictionary<string,string>.FindEntry(string key)
mscorlib.dll!System.Collections.Generic.Dictionary<System.__Canon,System.__Canon>.ContainsKey(System.__Canon key)
ClassName.SettleDueDate.set(string value)
ClassName.SettleDueDate.set(string value)
ClassName.SettleDueDate.set(string value)
// Infinite recursion of this call

このスタック トレースにより、STPP.SettleDueDate の getter/setter を間違って実装したと思いました。私はそれらをチェックし、バッキング変数などは正しかったです(ゲッター/セッターのループの通常の原因は理解しています)。PrepareRequest()さらにデバッグすると、次の行が呼び出されたときにスタック トレースが実際に生成されたことがわかりました。

writer.WriteElementString("accounttypedescription", STPPData.AccountTypeDescription);

セッターで使用するバッキング プロパティを作成したが、ゲッターでバッキング プロパティを使用していなかったため、STPPData.AccountTypeDescription のゲッターを正しく実装していないことがわかりました。

public string AccountTypeDescription
{
    get {
        //return AccountTypeDescription; // This would cause infinite recursion.
        return _accountTypeDescription; // This fixed the StackOverflowException
    }
    // setter omitted for clarity (it is in the examples above)
}

私の質問は:

バグが実際には AccountTypeDescription.get() 内にあるのに、StackOverflowException のスタック トレースが SettleDueDate.set() を示しているのはなぜですか?

注: 私は C# を初めて使用し、LAMP のバックグラウンドを持っています。コードを少し簡略化しましたが、重要なものを削除したとは思いません。

4

2 に答える 2

1

以下は、問題を絞り込むための簡単なデバッグ手順です。

  • SettleDueDate プロパティでクラスを開く
  • SettleDueDate のプロパティ名を右クリックします。
  • メニュー項目「すべての参照を検索」をクリックします
  • SettleDueDate が設定されているすべての場所、つまり 'SettleDueDate = "Something or other"' にブレークポイントを追加します。
  • アプリケーションを実行し、ブレークポイントがヒットしたときに連続して複数回ヒットするまで続行します
  • 問題のあるポイントが見つかり、コードがブレークポイントにある場合は、続行する代わりに、ステップ アウト コマンドとステップ オーバー コマンドを使用して、スタックをさかのぼってトレースし、再帰的に割り当てられている場所を見つけます。
于 2012-12-07T18:49:10.733 に答える
1

このコードは非常に断片的であり、すべての接続を完全に理解しているとは言えません。ClassName == STPPData および SecondClass == STPPXml と仮定していますか? それにもかかわらず、VS2010 と .NET 4 を使用してこのバグを再現しようとしましたが、できませんでした。スタック トレースは、AccountTypeDescription.set() 内でのみ無限再帰を示しました。何かが欠けているに違いない。

まず、スタック トレースの次の行は非常に興味深いものです。

mscorlib.dll!string.GetHashCode()
mscorlib.dll!System.Collections.Generic.GenericEqualityComparer<System.__Canon>.GetHashCode(SYstem.__Canon obj)
mscorlib.dll!System.Collections.Generic.Dictionary<string,string>.FindEntry(string key)
mscorlib.dll!System.Collections.Generic.Dictionary<System.__Canon,System.__Canon>.ContainsKey(System.__Canon key)

これらは、SettleDueDate.set() の無限呼び出しだけでなく、その内部を明確に示しているように見えます。ディクショナリとハッシュ ルックアップが存在します。間違いなくどこかにバグがあります。ただし、ソース コードにはバグが含まれていないという予感があります。@Bryanの回答を参照して、コード内の呼び出し場所ではなく、SettleDueDateセッターブレークポイントを設定しましたか? Visual Studio を使用している場合は、この機能を使用して例外を中断することもできます。

あなたが Web サービスを扱っているのを見て、すぐにプロキシ クラスを思い浮かべました。それらはあなたが書いたコードに非常によく似ているかもしれませんが、あなたが書いたコードではありません。それらは古くなる可能性があります。この例外が、作成およびコンパイルしたコードによってスローされたという確信はありますか?

2 番目に、Web サービスではシリアライゼーションも思い浮かびます。これは、多くの場合、知らないうちにプロパティのゲッターとセッターを呼び出すプロセスです。過去に、WCF が IEnumerables をシリアル化しようとしてコードが問題なくてもひどく失敗するという問題がありました。

余談ですが、私のシステムでは、この行はコンパイルされませんでした:

if (!_accountTypes.Contains(value))

これは、Mono を使用しているのか、それとも私とは異なる IDE を使用しているのか疑問に思いました。結局、あなたはLAMPの男です=)

これが(まだ)本当の答えではないことはわかっていますが、これについてどう思うのですか?他に共有できる詳細はありますか?

于 2013-03-08T20:39:25.893 に答える