似乎如果方法上只有一个类型数组的参数
传递给我的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;
}
}
}
}
答案 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}}调用数组,因为你预期。