-1

メソッドに配列型のパラメーターが 1 つしかない場合、メソッドに渡されるパラメーターの値はLogException()もはや配列ではないようです。

メソッドに複数のパラメーターがある場合、または 1 つのパラメーターが配列でない場合、期待どおりに機能します。しかし、配列を渡そうとすると、配列の最初の値が渡されたパラメーターになるようです。

すべてのコメントは、問題を説明および表示するためにインライン化されています。問題は最初に「ポイント 4」で発生します。間違った値が見つかると、例外に格納されているパラメーター情報が間違っています。他の点は、その後に生じる混乱を明確にします。私はそれを解決する方法がわかりません。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Testapp
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] tmp1 = new string[2];
            tmp1[0] = "val1";
            tmp1[1] = "val2";

            //please look at point 1
            TestMethod1(tmp1);
            //please look at point 2
            TestMethod2(tmp1, "just a value");
        }


        private static void TestMethod1(string[] ArrayType)
        {
            try
            {
                throw new System.Exception("blow");
            }
            catch (System.Exception ex)
            {
                LogException(ex, ArrayType);

                foreach (System.Collections.DictionaryEntry entry in ex.Data)
                {
                    string tmp1 = entry.Key.ToString();
                    string tmp2 = entry.Value.ToString();
                    //point 1 (for param:ArrayType... well there is only 1 parameter)
                    //the value of tmp2 = val1
                    //and should be {val1,val2}
                    System.Diagnostics.Debugger.Break();
                }
            }
        }
        private static void TestMethod2(string[] ArrayType, string StringType)
        {
            try
            {
                throw new System.Exception("blow");
            }
            catch (System.Exception ex)
            {
                LogException(ex, ArrayType, StringType);

                foreach (System.Collections.DictionaryEntry entry in ex.Data)
                {
                    string tmp1 = entry.Key.ToString();
                    string tmp2 = entry.Value.ToString();

                    //point 2 (for param:ArrayType)
                    //the value of tmp2 = {val1,val2} (correct, this what i expected)
                    //please look at point 3
                    System.Diagnostics.Debugger.Break();
                }
            }
        }

        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
        public static void LogException(System.Exception Exception, params object[] args)
        {
            using (CallerInfo callerinfo = new CallerInfo(1))
            {
                callerinfo.AddParameterInfo(Exception, args);
            }
        }

        private class CallerInfo : IDisposable
        {
            private System.Reflection.ParameterInfo[] parameterinfos = null;
            private string identifiername = string.Empty;
            private string assemblyname = string.Empty;

            public void AddParameterInfo(System.Exception Exception, params object[] sourceargs)
            {
                if (parameterinfos == null) return;

                string locationname = identifiername + " - param:";
                foreach (System.Reflection.ParameterInfo ParameterInfo in parameterinfos)
                {
                    string KeyName = locationname + ParameterInfo.Name;
                    object parameter = null;
                    try
                    {
                        System.Diagnostics.Debugger.Break();
                        //point 4
                        //the next line goes wrong when there is ONLY 1 parameter on a method of type array
                        parameter = sourceargs[ParameterInfo.Position];

                    }
                    catch
                    {
                        parameter = null;
                    }
                    if (parameter == null)
                    {

                        if (!Exception.Data.Contains(KeyName))
                        {
                            Exception.Data.Add(KeyName, "*NULL*");
                        }
                    }
                    else
                    {
                        if (ParameterInfo.ParameterType.IsArray)
                        {
                            //point 3
                            //this is where i got confused
                            //the check if (ParameterInfo.ParameterType.IsArray) is returning true.. correct the first parameter in both methods are of type array
                            //however for TestMethod1 (that is having ONLY 1 parameter) the value of parameter (see point 4) is NOT an array anymore?????
                            //for TestMethod2 (that is having 2 parameters, but the SAME first parameter as passed in TestMethod1) the value of parameter (see point 4) is an array what is correct
                            System.Diagnostics.Debugger.Break();
                            if (parameter.GetType().IsArray)
                            {
                                string arrayvaluelist = "{";
                                try
                                {
                                    System.Collections.ArrayList arraylist = new System.Collections.ArrayList((System.Collections.ICollection)parameter);
                                    foreach (object arrayitem in arraylist)
                                    {
                                        if (arrayitem == null) { arrayvaluelist = arrayvaluelist + "*NULL*,"; continue; }
                                        arrayvaluelist = arrayvaluelist + arrayitem.ToString() + ",";
                                    }
                                    arrayvaluelist = arrayvaluelist.Substring(0, arrayvaluelist.Length - 1);
                                    arrayvaluelist = arrayvaluelist + "}";
                                }
                                catch
                                {
                                    arrayvaluelist = "Error in constructing the arrayvalue list for parameter: " + ParameterInfo.Name;
                                }
                                if (!Exception.Data.Contains(KeyName))
                                {
                                    Exception.Data.Add(KeyName, arrayvaluelist);
                                }
                            }
                            else
                            {
                                //point 5 -- i shouldn't be here !!!!
                                System.Diagnostics.Debugger.Break();
                                if (!Exception.Data.Contains(KeyName))
                                {
                                    Exception.Data.Add(KeyName, parameter.ToString() + " warning wrong value is returned.");
                                }
                            }
                        }
                        else
                        {
                            if (!Exception.Data.Contains(KeyName))
                            {
                                Exception.Data.Add(KeyName, parameter.ToString());
                            }
                        }
                    }
                }
            }

            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
            public CallerInfo(int Level)
            {
                try
                {
                    System.Diagnostics.StackTrace stackTrace = new System.Diagnostics.StackTrace();
                    System.Reflection.MethodBase methodbase = stackTrace.GetFrame(Level + 1).GetMethod();
                    parameterinfos = methodbase.GetParameters();
                    assemblyname = methodbase.ReflectedType.Assembly.ManifestModule.Name;
                    identifiername = methodbase.ReflectedType.FullName + "." + methodbase.Name;
                }
                catch
                {
                    //broken
                }
            }
            void IDisposable.Dispose()
            {
                parameterinfos = null;
            }
        }
    }
}
4

1 に答える 1

1

最小限、完全、かつ検証可能な優れたコード例をありがとうございます。当初の質問は特に明確ではありませんでしたが、優れた MCVE を使用することで、正確な問題を簡単に理解できるようになりました。(3 人の異なる人が質問の最も重要な部分に目を向けなかったのは残念です…完全に明確ではないが完全なコードを含む質問であっても、最悪の質問が賛成票を投じられたようです。質問の重要な部分—反対票を投じられます:( ).


とにかく、ここでの問題はparams、パラメーター自体が配列であるという事実と組み合わせて を使用することです。実際の意味を理解することが重要paramsです。そのように宣言されたパラメーターは実際には配列であり、通常の配列パラメーターのすべての通常の規則に従います。提供される唯一のことparamsは、オプションで複数の引数値を指定して配列にデータを入力することができ、コンパイラがそれらの値を取得して配列に結合することです。

問題が発生したのは、引数値として配列を指定すると、コンパイラはそれをメソッドに対して宣言された実際の配列引数として扱い、追加の作業を行わないことです。

object[]の代わりにを渡していた場合、問題はより明白になる可能性がありますstring[]object[]その場合、配列全体がメソッドのパラメーターの型と正確に一致しているLogException()ため、別の に格納されるのではなく、直接渡されることが簡単にわかりますobject[]。たまたま、C# の配列は "共変" です。この場合の主な意味は、メソッドが配列を想定している場合、渡された配列の要素が型を継承するため、任意のobject[]型の配列を渡すことができるということです。object

そのため、値を渡すと、C# コンパイラはこれをメソッドのパラメーターの型ArrayTypeと互換性があると認識し、配列自体を単一の要素として に格納するのではなく、そのパラメーターとして渡すだけです。その後、パラメータ値を取得しようとすると、メソッドが 2 つの異なるパラメータ、つまり と の 2 つの値を持つメソッドによって呼び出されたように見えます。LogException()object[]object[]LogException()string"val1""val2"

それで、これを修正する方法は?非常に簡単です。呼び出しのために、値の配列の性質を C# コンパイラから隠す必要があります。

LogException(ex, (object)ArrayType);

つまり、メソッドで、を呼び出すときに値をTestMethod1()キャストします。これにより、コンパイラは配列オブジェクトを単純な値として扱い、値の型がパラメーターの型と一致するのを防ぎ、期待どおりに呼び出し用の新しい配列に値を格納します。ArrayTypeobjectLogException()objectparams object[] argsobject[]

于 2016-07-26T17:04:00.213 に答える