传递第一个数组元素而不是整个数组

时间:2016-07-26 15:27:47

标签: c# params-keyword

似乎如果方法上只有一个类型数组的参数 传递给我的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;
            }
        }
    }
}

1 个答案:

答案 0 :(得分:1)

非常感谢你的好Minimal, Complete, and Verifiable code example。尽管最初措辞的问题并不是特别明确,但拥有良好的MCVE确保了可以轻松理解确切的问题。 (令人遗憾的是,三个不同的人并没有费心去看问题中最重要的部分......似乎最糟糕的问题起了投票,即使是一个不完全清楚的问题,但其中包括完整代码 - 任何问题中最重要的部分 - 得到了投票:()。


无论如何,这里的问题是你使用params以及参数本身是一个数组的事实。理解params实际意味着什么很重要:以这种方式声明的参数实际上是一个数组,并遵循常规数组参数的所有常规规则。 params给你的唯一的事情就是你可以通过提供多个参数值可选地填充数组,编译器将获取这些值并将它们组合成一个数组。

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

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

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

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

"val2"

即。在LogException(ex, (object)ArrayType); 方法中,在调用TestMethod1()时将ArrayType值转换为object。这将强制编译器将数组对象视为简单的LogException()值,从而阻止它将值的类型与object参数类型匹配,并将值存储在新的params object[] args中1}}调用数组,因为你预期。