2016-07-26 3 views
-1

Il semble que s'il n'y a qu'un seul paramètre de type array sur une méthode , la valeur du paramètre passé à ma méthode LogException() n'est plus un tableau.Le premier élément de tableau est passé à la place du tableau entier

Lorsqu'il y a plusieurs paramètres dans une méthode ou si le paramètre n'est pas un tableau, cela fonctionne comme prévu. Mais quand j'essaye de passer un tableau, il semble que la première valeur du tableau devienne le paramètre qui a été passé.

Tous les commentaires sont insérés pour expliquer et montrer le problème. Le problème apparaît d'abord au "point 4"; Une fois la valeur erronée trouvée, les informations de paramètres stockées dans mon exception sont erronées. Les autres points clarifient la confusion qui en résulte. Je n'ai aucune idée de comment le résoudre.

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; 
      } 
     } 
    } 
} 

Répondre

1

Merci beaucoup pour le bon Minimal, Complete, and Verifiable code example. Bien que la question initialement formulée ne soit pas particulièrement claire, un bon MCVE a permis de comprendre facilement le problème exact. (Il est regrettable que trois personnes différentes n'aient pas pris la peine de regarder la partie la plus importante de la question & hellip, il semble que les plus mauvaises questions se lèvent, même si une question pas entièrement claire, mais qui comprend le code le plus une partie importante de toute question — se met-a voté :().


quoi qu'il en soit, la question est ici votre utilisation de params conjointement avec le fait que le paramètre lui-même est un tableau. Il est important de comprendre ce que params fait signifie que le paramètre déclaré de cette façon est en fait un tableau, et suit toutes les règles normales pour les paramètres de tableau réguliers. peut éventuellement remplir le tableau en fournissant plusieurs valeurs d'arguments, et le compilateur prendra ces valeurs et les combiner dans un tableau. En cas de problème, si vous fournissez un tableau en tant que valeur de l'argument, le compilateur le considère comme l'argument tableau réel qui a été déclaré pour la méthode et ne fait aucun travail supplémentaire.

Le problème aurait pu être plus évident si vous aviez passé un object[] au lieu d'un string[]. Dans ce cas, vous pouvez facilement voir que l'ensemble du tableau object[] correspond exactement au type de paramètre de la méthode LogException(), et est donc transmis directement plutôt que d'être stocké dans un autre object[]. En l'occurrence, les tableaux en C# sont "covariants". Dans ce cas, la principale chose qui signifie que si une méthode attend un tableau object[], vous pouvez lui passer un tableau de n'importe quel type, car les éléments du tableau transmis héritent le type object.

Ainsi, lorsque vous passez la valeur ArrayType, le compilateur C# reconnaît cela comme compatible avec le type de paramètre de la méthode LogException()object[] et passe juste le tableau lui-même comme ce paramètre, plutôt que de le stocker comme un seul élément dans un object[]. Ensuite, lorsque vous récupérez les valeurs des paramètres, il semble que votre méthode LogException() ait été appelée par une méthode avec deux paramètres différents, à savoir deux valeurs string de "val1" et "val2", respectivement.

Alors, comment résoudre ce problème?Très simple: il vous suffit de cacher la nature du tableau de la valeur du compilateur C# dans le but de l'appel:

LogException(ex, (object)ArrayType); 

à savoir Dans votre méthode TestMethod1(), attribuez la valeur ArrayType à object lorsque vous appelez le LogException(). Cela forcera le compilateur à traiter l'objet tableau comme une valeur object simple, l'empêchant de faire correspondre le type de valeur au type de paramètre params object[] args et de stocker la valeur dans un nouveau tableau object[] pour l'appel comme prévu.