-1

似乎如果方法上只有一个数组类型的参数,则传递给我的LogException()方法的参数值不再是数组。

当一个方法有多个参数时,或者如果一个参数不是数组,它会按预期工作。但是当我尝试传递一个数组时,似乎数组的第一个值变成了传递的参数。

所有评论都内联以解释和显示问题。问题首先出现在“第 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 可以确保确切的问题很容易被理解。(不幸的是,三个不同的人没有费心去看问题中最重要的部分……似乎最糟糕的问题得到了投票,即使是一个不完全清楚但包含完整代码的问题——最任何问题的重要部分——被否决:()。


无论如何,这里的问题是您params结合使用参数本身是一个数组这一事实。了解params实际含义很重要:以这种方式声明的参数实际上一个数组,并且遵循常规数组参数的所有常规规则。唯一params给您的是,您可以选择通过提供多个参数值来填充数组,编译器将获取这些值并将它们组合成一个数组。

您遇到麻烦的地方是,如果您提供一个数组作为参数值,编译器会将其视为为该方法声明的实际数组参数,并且不会做任何额外的工作。

如果您传递的object[]是 a 而不是 a ,问题可能会更加明显string[]。在这种情况下,您可以很容易地看到整个object[]数组与LogException()方法的参数类型完全匹配,因此直接传递而不是存储在另一个object[]. 碰巧的是,C# 中的数组是“协变的”。在这种情况下,主要的意思是如果一个方法需要一个object[]数组,你可以传递一个任意类型的数组,因为传递的数组的元素继承了该object类型。

因此,当您传递ArrayType值时,C# 编译器将其识别为与LogException()方法的object[]参数类型兼容,并将数组本身作为该参数传递,而不是将其作为单个元素存储在object[]. 然后稍后当您检索参数值时,似乎您的LogException()方法已被具有两个不同参数的方法调用,即分别为和的两个string值。"val1""val2"

那么,如何解决这个问题?非常简单:您只需要从 C# 编译器中隐藏值的数组性质即可进行调用:

LogException(ex, (object)ArrayType);

即在您的TestMethod1()方法中,将ArrayType值转换为object调用LogException(). 这将强制编译器将数组对象视为一个简单object值,阻止它将值的类型与params object[] args参数类型匹配,并将该值存储在一个新object[]数组中以供调用,如您所期望的那样。

于 2016-07-26T17:04:00.213 回答