2008-10-31 8 views
26

J'ai une méthode avec un paramètre out qui essaie de faire une conversion de type. En gros:.NET: Comment obtenez-vous le type d'un objet nul?

public void GetParameterValue(out object destination) 
{ 
    object paramVal = "I want to return this. could be any type, not just string."; 

    destination = null; // default out param to null 
    destination = Convert.ChangeType(paramVal, destination.GetType()); 
} 

Le problème est que généralement quelqu'un appellerait cela comme:

string output; 
GetParameterValue(output); 

Cela échouera en raison de:

destination.GetType() 

destination est nulle, nous ne pouvons pas appeler .GetType() dessus. Nous ne pouvons pas non plus appeler:

typeof(destination) 

car la destination est un nom de variable et non un nom de type.

Alors, est-il possible d'obtenir le type d'un objet qui est défini sur null? Je pense qu'il devrait y avoir un moyen de savoir quel est le type d'un emplacement de stockage sans qu'on lui attribue quoi que ce soit.


Juste pour donner un peu plus d'infos, je suis en train de faire une méthode utilitaire qui va récupérer les paramètres de sortie d'une procédure stockée Oracle. Le problème est que DbParameter.Value est de type objet.

Quel serait idéal serait pour les développeurs de faire quelque chose comme:

string val = GetParameterValue("parameterName"); 

La chose remarquable est qu'il n'y a pas coulée de types. Dans la pratique, vous ne connaissez pas le lparam du « égaux », alors je suis allé avec:

string val; 
GetParameterValue("parameterName", out val); 

Et figuré dans la méthode, je sais le type de destination de la variable de sortie. Je suppose que c'était une mauvaise hypothèse. Comme alternative, je l'ai écrit aussi la méthode:

public T GetParameterValue<T>(string paramName) 

Ainsi, les développeurs peuvent faire:

string val = GetParameterValue<string>("parameterName"); 

Je trouve la déclaration explicite « chaîne » d'être répétitif, d'autant plus que dans la pratique, la destination probablement une propriété d'objet et l'oracle type de données pourraient changer (penser ORM):

MyObj.SomeProp = GetParameterValue<MyObj.SomeProp.GetType()>("parameterName"); 

Mais encore une fois, si MyObj.SomeProp est nul, que .GetType() appel échoue. La machine virtuelle doit connaître le type de MyObj.SomeProp, même si son null, non? ou bien comment attraperait-il des exceptions?


Pour résoudre partiellement mon problème, je peux faire:

MyObj.SomeProp = GetParameterValue<typeof(MyObj).GetField("SomeProp").GetType()>("parameterName"); 

L'idée était d'avoir de ne pas utiliser explicitement le type dans plus d'un endroit, de sorte que si les changements de type de données , il doit uniquement être modifié dans l'objet de destination (MyObj.SomeProp) et dans le DB. Il doit y avoir un meilleur moyen ...

Répondre

29

Ainsi est-il possible d'obtenir le type d'un objet qui est défini sur null? Je pense qu'il devrait y avoir un moyen de savoir quel est le type d'un emplacement de stockage sans qu'on lui attribue quoi que ce soit.

Pas nécessairement. Le meilleur que vous pouvez dire est que c'est un object. Une référence null ne pointe pas vers un emplacement de stockage, il n'y a donc aucune métadonnée à partir de laquelle il peut effectuer cette détermination.

Le mieux que vous pouvez faire est de changer pour être plus générique, comme dans:

public void GetParameterValue<T>(out T destination) 
{ 
    object paramVal = "Blah"; 
    destination = default(T); 
    destination = Convert.ChangeType(paramVal, typeof(T)); 
} 

Le type de T peut être déduit, de sorte que vous ne devriez pas besoin de donner un paramètre de type à la méthode explicitement.

+0

Bonne suggestion! Le second argument devrait être typeof (T) et pas typeof (T) .GetType(). – Hallgrim

+0

Malheureusement, le code "out T dest" ne compile pas car il ne trouve pas le type "T". Je vais rechercher cette option un peu plus ... – CodingWithSpike

+0

@ rally25rs: le T est un type générique, dans ce cas ce qui est suggéré est que vous utilisez des génériques pour déterminer le type de la méthode. –

0

Dans votre exemple ce serait null de type System.Object.

Est-ce que votre exemple compile même? Je reçois une erreur "impossible de convertir d'une chaîne 'à l'autre'".

+0

mes tests unitaires pour qu'il ne compile, mais j'ai eu la réelle hors jeu variable à un objet pas une chaîne. – CodingWithSpike

2

Le type de votre variable de destination est toujours System.Object. Vous pouvez juste retourner

Convert.ChangeType(paramVal, System.Object). 
3

Actuellement, vous n'avez aucun moyen de savoir ce qui est passé dans la méthode. Vous pouvez le convertir en une méthode générique. Comme ceci:

public void GetParameterValue<T>(out T destination) { ... }

+0

J'ai effectivement fait cette même méthode comme un deuxième moyen d'obtenir les mêmes données. Ca fonctionnait, donc je ne l'ai pas inclus dans mon post :) – CodingWithSpike

0

Je ne pense pas qu'il soit possible d'obtenir le type lorsque la valeur est nulle. De plus, puisque vous appelez dans GetParameterValue, le mieux que vous puissiez faire (lorsque la valeur est null) est d'obtenir le type du paramètre "destination" qui est "object". Vous pourriez envisager passer le type comme paramètre à GetParameterValue où vous avez plus d'informations, telles que:

public void GetParameterValue(Type sourceType, out object destination) { //... } 
8

Il est possible si vous ne me dérange pas de déclarer votre méthode comme générique. Essaye ça.

class Program 
{ 
    public static void GetParameterValue<T>(out T destination) 
    { 
     Console.WriteLine("typeof(T)=" + typeof(T).Name); 
     destination = default(T); 
    } 
    static void Main(string[] args) 
    { 
     string s; 
     GetParameterValue(out s); 
     int i; 
     GetParameterValue(out i); 
    } 
} 
0

S'il n'y a pas d'instance, il n'y a pas de type d'instance. Le mieux que vous pouvez faire est d'utiliser le type de la référence, ce qui signifie que si vous avez une référence d'objet (comme dans la méthode dans la question), le type de référence est objet.


Vous ne devriez pas essayer de convertir une instance nulle d'un type dans une instance nulle d'un autre type ...

1

@ Rally25s:

string val; 
GetParameterValue("parameterName", out val); 

On ne sait pas à partir de votre message (dans les réponses) quel était le problème avec celui-là. Si déclaré comme:

void GetParameterValue<T>(string parameterName, out T val) { } 

Que l'appel, comme vous l'avez écrit ci-dessus, ne fonctionnera (vous n'avez pas besoin de spécifier le type). Je suppose que cela n'a pas fonctionné pour vous parce que vous ne pouvez pas utiliser une propriété comme paramètre "out".Le chemin autour est d'utiliser les deux méthodes:

T GetParameterValue<T>(string parameterName, T ununsed) { } 

Ce serait appelé comme ceci:

MyObj.SomeProp = GetParameterValue("parameterName", MyObj.SomeProp); 

qui est plutôt kludgey, mais pas la méthode pire présenté.


Une autre méthode, que je l'ai utilisé en C++, mais ne l'ai pas encore essayé en C#, est d'avoir GetParameterValue() un objet vous propre conception, puis mettre en œuvre un certain nombre d'opérateurs de la distribution implicites pour ça.

class ParameterHelper 
{ 
    private object value; 
    public ParameterHelper(object value) { this.value = value; } 

    public static implicit operator int(ParameterHelper v) 
    { return (int) v.value; } 

} 
ParameterHelper GetParameterValue(string parameterName); 

MyObj.SomeProp = GetParameterValue("parameterName"); 
+0

Marcus a édité sa réponse après l'avoir lue. A l'origine il avait "GetParameterValue (out T dest)", au lieu de "GetParameterValue (...)". Sa réponse éditée est correcte. – CodingWithSpike

0

Au niveau théorique est pas vraiment nul le même que celui d'un pointeur vide dans C, ce qui veut dire qu'il est titulaire d'une adresse mémoire et qui est-il? Si c'est le cas, il est similaire au cas d'une division par zéro en mathématiques où le résultat est indéfini.

On pourrait faire ce qui suit pour cette ligne:

string val = GetParameterValue<string>("parameterName"); 

Il suffit d'enlever la première chaîne et maintenant il n'y a pas la répétition:

var val = GetParameterValue<string>("parameterName"); 

Pas nécessairement ce que vous cherchez, si il y a la question de comment interprète-t-on nul?

+0

Le problème n'était pas "quel est leur type de null", mais "quel est le type de cet emplacement de stockage qui est actuellement défini sur null" So "DataTable dt = null", la VM sait toujours que dt est de Type DataTable . – CodingWithSpike

+0

En définissant une variable à null, il ne pointe sur rien, est mon point. Si vous avez 3 classes dont chacune est dérivée d'une autre, par ex. A est dérivé de B et B est dérivé de C, et vous aviez une variable de type B qui est assignée nulle, ne pourriez-vous pas affecter une variable de type A à cela ou pas? –

0
//**The working answer** 

//**based on your discussion eheheheheeh** 

public void s<T>(out T varName) 
{ 
    if (typeof (T) == typeof(HtmlTable)) 
    { 
     //////////  
    } 

} 

protected void Page_Load(object sender, EventArgs e) 
{ 
    HtmlTable obj=null ; 
    s(out obj);  
} 
-1

http://msdn.microsoft.com/en-us/library/58918ffs.aspx

ou

private Hashtable propertyTable = new Hashtable(); 

public void LoadPropertyTypes() 
{ 
    Type t = this.GetType(); 

    System.Reflection.MemberInfo[] memberInfo = t.GetMembers(); 

    foreach (System.Reflection.MemberInfo mInfo in memberInfo) 
    { 
     string[] prop = mInfo.ToString().Split(Convert.ToChar(" ")); 
     propertyTable.Add(prop[1], prop[0]); 
    } 
} 
public string GetMemberType(string propName) 
{ 
    if (propertyTable.ContainsKey(propName)) 
    { 
     return Convert.ToString(propertyTable[propName]); 
    } 
    else{ 
     return "N/A"; 
    } 
} 

de cette façon que nous pouvons utiliser l'interrupteur pour gérer différents types de propriété.

7

La méthode d'extension suivante retourne comme il a été déclaré, quel que soit son contenu du type de son paramètre :

using System; 

namespace MyNamespace 
{ 
    public static class Extensions 
    { 
     /// <summary> 
     /// Gets the declared type of the specified object. 
     /// </summary> 
     /// <typeparam name="T">The type of the object.</typeparam> 
     /// <param name="obj">The object.</param> 
     /// <returns> 
     /// A <see cref="Type"/> object representing type 
     /// <typeparamref name="T"/>; i.e., the type of <paramref name="obj"/> 
     /// as it was declared. Note that the contents of 
     /// <paramref name="obj"/> are irrelevant; if <paramref name="obj"/> 
     /// contains an object whose class is derived from 
     /// <typeparamref name="T"/>, then <typeparamref name="T"/> is 
     /// returned, not the derived type. 
     /// </returns> 
     public static Type GetDeclaredType<T>(
      this T obj) 
     { 
      return typeof(T); 
     } 
    } 
} 

Comme il est une méthode d'extension, son argument peut être une référence nulle, et tout des travaux suivants: OK

string myString = "abc"; 
object myObj = myString; 
Type myObjType = myObj.GetDeclaredType(); 

string myNullString = null; 
object myNullObj = myNullString; 
Type myNullObjType = myNullObj.GetDeclaredType(); 

Notez que myObjType et myNullObjType seront tous deux mis à System.Object, non System.String.

Si vous voulez vraiment le type de contenu de obj quand il est non nul, puis modifiez la ligne return à:

return (obj != null) ? obj.GetType() : typeof(T); 
+0

Vous obtiendrez une RuntimeBinderException si obj est déclaré comme dynamique. –

Questions connexes