3

コードが呼び出されている時間を教えてくれる内部メッセージングシステムを作成したいと思っています。SystemMessageクラスにIDisposableを実装させるために、使いやすさを考えていました。

SystemMessageのコンストラクター中にタイムスタンプを設定し、Disposeが呼び出された場合、期間を把握できました。

問題は、オブジェクトをGC化したくないということです。MessageCollectionの一部として残しておいてほしい。

IDisposableの目的の機能を踏まずに、Usingステートメントの使いやすさを提供できるC#の別の構造はありますか?

Using (message = Collection.CreateNewMessage("FileDownlading"))
{
    // I wonder how long it is taking me to download this file in production?
    // Lets log it in a message and store for later pondering.
    WebClass.DownloadAFile("You Know This File Is Great.XML");
}
// we fell out of the using statement, message will figure out how long
// it actually took to run.
// This was clean and easy to implement, but so wrong?
4

12 に答える 12

5

問題は、オブジェクトを GC したくないということです。MessageCollection の一部として残しておきたいのです。

Dispose を呼び出しても、オブジェクトが GC されることはありません。これは、GC がスイープを実行し、何も参照していない場合に発生します。MessageCollection を介してオブジェクトを参照している場合は、残ります。

Dispose はファイナライズされないようにすることができますが、Dispose を使用してリソースをクリーンアップするわけではないため、ファイナライザーはなく、気にする必要はありません。

したがって、実際に唯一の問題は、破棄するリソースがないにもかかわらず、カルスに IDisposable を実装させることに関する混乱するセマンティクスです。

個人的には、問題とは思いません。コンシューマーが Dispose を呼び出すと、問題なく時間のログが記録されます。そうしないと、アイテム スタンプを取得できず、最悪の場合、FxCop 違反が発生します。

ただし、これは少し直感的ではありません。したがって、これが公共の用途である場合は、次のようなより見つけやすい代替手段を提供することをお勧めします。

// C# 3+ lambda syntax
Collection.CreateNewMessage("FileDownlading", () => {
    // I wonder how long it is taking me to download this file in production?    
    // Lets log it in a message and store for later pondering.    
    WebClass.DownloadAFile("You Know This File Is Great.XML");
});

// C# 2 anonymous delegate syntax
Collection.CreateNewMessage("FileDownlading", delegate() {
    // I wonder how long it is taking me to download this file in production?    
    // Lets log it in a message and store for later pondering.    
    WebClass.DownloadAFile("You Know This File Is Great.XML");
});

// Method
void CreateNewMessage(string name, Action action) {
   StopWatch sw = StopWatch.StartNew();
   try {
      action();
   } finally {
      Log("{0} took {1}ms", name, sw.ElapsedMilliseconds);
   }
}

代わりに Action デリゲートを実行して時間を計ります。

于 2009-05-01T18:47:52.023 に答える
2

クロージャに似たものをお探しですか?

http://en.wikipedia.org/wiki/Closure_(computer_science)

あなたは何かを偽造することができます...このように...

private TimeSpan GetDuration(Action a)
        {
            var start = DateTime.Now;
            a.Invoke();
            var end = DateTime.Now;
            return end.Subtract(start);
        }

        public void something()
        {
            string message;
            var timeSpan = GetDuration(() => { message = "Hello"; } );
        }
于 2009-05-01T18:35:50.930 に答える
1

まあ、これはオールディーズですが、他の誰も私が最高のスタイルだと思う答えを投稿していないようです(私があなたの要件を理解していることから):

using (MessageTimer timer = Collection.CreateNewTimedMessage("blabla"))
{
   // ...
}

重要な点はCreateNewTimedMessage、副作用として半永久的なMessageオブジェクトを作成して保存する可能性があるが、ブロックのスコープを存続しない一時的なタイミングオブジェクト(または同様のメカニズムを使用する)を返すことです。(を参照しても問題ありませんが、その逆はできません。)StopWatchusingMessageTimerMessage

したがって、MessageTimerオブジェクトを破棄すると、最後の時間をどこかに記録するという副作用が発生する可能性がありますが、オブジェクト自体は存続したり、復活したりすることはありません。usingあなたが本当にオブジェクトを処分しているので、これは構造の乱用ではありません。

(の実際の実装は、MessageTimerおそらくJoeの答えに似ています。)

于 2011-04-06T07:37:51.970 に答える
1

私は最近これを見てきました、そしておそらくPostSharpはあなたを助けることができます。メソッドの開始時と停止時に呼び出される属性でメソッドを装飾できます。

http://www.postsharp.org/

それがあなたの好きなことをするかどうかはわかりませんが、調査する価値があり、あなたが切望している「シンタックスシュガー」が含まれています!

クリス

于 2009-05-01T22:30:24.250 に答える
1

ここであなたが望むのは使用ではないと思います。コンストラクターに時間を記録させてから、DownloadAFile が呼び出されたときに時間の差分をログに記録しないのはなぜですか? あなたの例では、例外がある場合、ファイルがダウンロードされたかのように例外の時間を記録します。

実際に動作を例のようにしたい場合は、try / finally ブロックを使用して、finally でログを記録してください。usingは、try/finally ブロックと への呼び出しのための単なる構文糖衣ですDispose

あなたの例に相当するコードは次のようになります。

try 
{
    var message = Collection.CreateNewMessage("FileDownlading"); 
    //...
    WebClass.DownloadAFile("You Know This File Is Great.XML");
}
finally 
{
    //You can change this to do logging instead.
    message.Dispose(); 
}
于 2009-05-01T18:13:57.003 に答える
1

DownloadAFile() 内で System.Diagnostics.Stopwatch を使用して、呼び出されるたびにタイミングを計ることができます。

または、DownloadAFile() への呼び出しの周りにストップウォッチ コードを追加するだけです (これをどのように動作させたいかによって異なります)。

この場合、IDisposable を使用することはお勧めできません。

于 2009-05-01T18:16:11.867 に答える
0

もともとIDisposableはC#で決定論的なクリーンアップを導入する方法として意図されていましたが、作成者と実装があなたが話していることに沿った何かにusing/機能を使用するのを見てきました。Dispose

個人的には、それが背後にある概念を損なうので、私はそれについてあまり満足していIDisposableませんが、代替手段がないので、それはあなたがどれだけオーソドックスになりたいかという問題に帰着します。なぜそれをやりたいのかは確かにわかりますが、そうすることで、IDisposableインターフェースの目的と重要性を説明するのが難しくなります。

于 2009-05-01T18:38:00.220 に答える
0

質問をもう一度確認した後、トレースメッセージ(デバッグ支援用)に似ている場合を除いて、問題のオブジェクトが実際にメッセージであるかどうかはわかりません。

これらの線に沿ってもっと何かを探しているなら、ここにデリゲートを使用した非常に大まかなアプローチがあります。基本的に、呼び出したいメソッドと時間ごとにデリゲートを作成し、デリゲートとメソッド引数を、メソッドの実際の呼び出しとその期間のタイミングを担当するヘルパーに渡します。

私の例の明らかな欠点は、それがあなたが探しているものであるかどうかが正確にわからなかったので、タイプセーフな引数を犠牲にしたことです。もう1つの問題は、デリゲートがまだないメソッドシグネチャを持つメソッドを呼び出すたびに、新しいデリゲートを追加する必要があることです。

using System;

namespace ConsoleApplication4
{
    class Program
    {
        static void Main(string[] args)
        {

            SomeCaller callerOne;
            YetAnotherCaller callerTwo;

            callerOne = new SomeCaller(SomeMethod);
            LogCallDuration(callerOne, new object[] { 15 });

            callerOne = new SomeCaller(SomeOtherMethod);
            LogCallDuration(callerOne, new object[] { 22 });

            callerTwo = new YetAnotherCaller(YetAnotherMethod);
            LogCallDuration(callerTwo, null);

            Console.ReadKey();
        }

        #region "Supporting Methods/Delegates"

        delegate void SomeCaller(int someArg);
        delegate void YetAnotherCaller();

        static void LogCallDuration(Delegate targetMethod, object[] args)
        {
            DateTime start = DateTime.UtcNow;
            targetMethod.DynamicInvoke(args);
            DateTime stop = DateTime.UtcNow;

            TimeSpan duration = stop - start;

            Console.WriteLine(string.Format("Method '{0}' took {1}ms to complete", targetMethod.Method.Name, duration.Milliseconds));

        }

        #endregion "Supporting Methods/Delegates"

        #region "Target methods, these don't have to be in your code"
        static void SomeMethod(int someArg)
        {
            // Do something that takes a little time
            System.Threading.Thread.Sleep(1 + someArg);
        }

        static void SomeOtherMethod(int someArg)
        {
            // Do something that takes a little time
            System.Threading.Thread.Sleep(320 - someArg);
        }

        static void YetAnotherMethod()
        {
            // Do something that takes a little time
            System.Threading.Thread.Sleep(150);
        }
        #endregion "Target methods"
    }
}
于 2009-05-01T19:55:04.450 に答える
0

これはusing構造の悪用であると主張できるので、IDisposableをこのように使用する共有クラスライブラリにパブリッククラスを実装することはおそらくないでしょう。

しかし、私はこの種のことが行われているのを見て、それがアプリケーションの内部に残っていれば問題ないと思います。

問題は、オブジェクトをGC化したくないということです。MessageCollectionの一部として残しておいてほしい。

私はこれをまったく理解していません。IDisposableはGCとは何の関係もありません。メッセージがMessageCollectionの要素として参照されている場合、メッセージは存続します。

メッセージクラスは、以下のサンプルのようになります。Disposeが呼び出された後、それは健在のままです。IDisposableを実装するほとんどのクラスは、Disposeが呼び出された後は使用できません。したがって、Disposeを呼び出した後にメンバーにアクセスすると、それらの実装はObjectDisposedExceptionをスローします。しかし、これは決して必須ではありません。

class Message : IDisposable
{
    private Stopwatch _stopwatch = Stopwatch.StartNew();
    private long _elapsedTicks;
    private string _message;

    public Message(string message)
    {
        _message = message;
    }

    public void Dispose()
    {
       _elapsedTicks = _stopwatch.ElapsedTicks;
       ... anything else including logging the message ...
    }

    ...
}
于 2009-05-01T18:42:45.063 に答える
0

「using」ステートメントは、実際には、オブジェクトが IDisposable を実装することを期待する Try/Finally にコンパイルされます。「using」は、言語によって提供される単なるショートカットです。

あなたの目的のために、特にオブジェクトをリサイクルしたいので、独自のインターフェースを書くことを検討します。メソッドを呼び出してタイミングを開始するときは、datetime.now の値を取得するだけです。タイマーを止めるとまた。次に、停止時間から開始時間を差し引くと、継続時間が得られます。

各クラス インスタンスで真の Timer オブジェクトを使用しないでください。タイマーは ThreadPool スレッドを使用します。これは、各メッセージが少なくとも 1 つのスレッドを消費することを意味します。システム内のメッセージが多すぎると、スレッドの切り替えの結果としてアプリケーションの速度が低下します。また、タイマーが適切に破棄されていないと、ThreadPool スレッドが解放されず、本質的にスレッド リークが発生します。

于 2009-05-01T19:02:34.810 に答える
0

あまり。あなたが来ることができる最も近いものは、このようなものです(とにかく、using()ステートメントの内部で起こっていることです:)

var message = Collection.CreateNewMessage("FileDownloading")

try
{
    WebClass.DownloadAFile("You Know This File Is Great.XML");
}
finally
{
    message.HowLongHaveIBeenAlive();
}
于 2009-05-01T18:16:13.830 に答える
0

using ステートメントは、使い終わったオブジェクトを破棄するためのものです。破棄されたオブジェクトを保持するという考えは、厳密には意図されたものではありません。次のような簡単なことを試してみてください。

message = Collection.CreateNewMessage("FileDownlading");
DateTime dtStart = DateTime.Now;
WebClass.DownloadAFile("You Know This File Is Great.XML");
DateTime dtEnd = DateTime.Now;

// perform comparison here to see how long it took.

// dispose of DateTimes
dtStart = dtEnd = null;
于 2009-05-01T18:16:20.400 に答える