【问题标题】:.NET : How do you get the Type of a null object?.NET:如何获得空对象的类型?
【发布时间】:2010-09-20 05:55:59
【问题描述】:

我有一个带有 out 参数的方法,它尝试进行类型转换。基本上:

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

问题是通常有人会这样称呼:

string output;
GetParameterValue(output);

这将失败,因为:

destination.GetType()

destination 为空,因此我们不能在其上调用.GetType()。我们也不能调用:

typeof(destination)

因为destination 是变量名而不是类型名。

那么有什么方法可以获取设置为 null 的对象的类型吗?我认为必须有一种方法可以在不分配任何内容的情况下知道存储位置的类型。


只是为了提供更多信息,我正在尝试制作一个实用方法来获取 Oracle 存储过程的输出参数。问题是DbParameter.Value 是对象类型。

对于开发人员来说,理想的做法是:

string val = GetParameterValue("parameterName");

值得注意的是没有类型转换。在实践中,您不知道“等于”的 lparam,所以我选择了:

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

在方法中,我会知道输出变量的目标类型。我想这是一个糟糕的假设。作为替代,我也写了方法:

public T GetParameterValue<T>(string paramName)

所以开发者可以这样做:

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

我发现明确的“字符串”声明是重复的,特别是因为在实践中,目标可能是对象属性和 oracle 数据类型可能会改变(想想 ORM):

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

但同样,如果 MyObj.SomeProp 为 null,则 .GetType() 调用将失败。 VM 必须知道MyObj.SomeProp 的类型,即使它为空,对吧?否则它将如何捕获强制转换异常?


要部分解决我自己的问题,我可以这样做:

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

整个想法是不必在多个位置显式使用 Type,因此如果数据类型发生变化,只需在目标对象 (MyObj.SomeProp) 和 DB 中进行更改。必须有更好的方法...

【问题讨论】:

    标签: c# .net types gettype


    【解决方案1】:

    那么有什么方法可以获取设置为 null 的对象的类型吗?我认为必须有一种方法可以在不分配任何内容的情况下知道存储位置的类型。

    不一定。你能说的最好的就是它是objectnull 引用不指向任何存储位置,因此没有元数据可以用来做出决定。

    您能做的最好的就是将其更改为更通用,如下所示:

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

    T 的类型是可以推断出来的,所以你不需要显式地给方法提供类型参数。

    【讨论】:

    • 好建议!第二个参数应该是 typeof(T) 而不是 typeof(T).GetType()。
    • 不幸的是,代码“out T dest”无法编译,因为它找不到类型“T”。我会再研究一下这个选项...
    • @rally25rs:T 是泛型类型,在这种情况下,建议您使用泛型来确定方法的类型。
    • 在 Marcus 编辑以将“”添加到方法签名后,此解决方案可以编译并运行!谢谢!
    • +1 - 这使我免于无知地将类型参数添加到我的方法中(我不想这样做)!
    【解决方案2】:

    如果您不介意将您的方法声明为泛型,这是可能的。试试这个。

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

    【讨论】:

      【解决方案3】:

      以下扩展方法返回其参数声明时的类型,无论其内容如何:

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

      由于这是一个扩展方法,它的参数可以是一个空引用,并且以下所有工作都可以:

      string myString = "abc";
      object myObj = myString;
      Type myObjType = myObj.GetDeclaredType();
      
      string myNullString = null;
      object myNullObj = myNullString;
      Type myNullObjType = myNullObj.GetDeclaredType();
      

      请注意,myObjTypemyNullObjType 都将设置为 System.Object,而不是 System.String。

      如果你真的想要obj不为null时的内容类型,那么将return这一行改为:

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

      【讨论】:

      • 如果 obj 被声明为动态的,你会得到一个 RuntimeBinderException。
      【解决方案4】:

      目前,您无法知道传递给该方法的内容。您可以将其转换为这样的通用方法:

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

      【讨论】:

      • 我实际上采用了与获取相同数据的第二种方法相同的方法。它工作正常,所以我没有将它包含在我的帖子中:)
      【解决方案5】:

      目标变量的类型始终为System.Object。你可以返回

      Convert.ChangeType(paramVal, System.Object).
      

      【讨论】:

        【解决方案6】:

        @Rally25s:

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

        您的消息(在答案中)不清楚该消息的问题是什么。如果声明为:

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

        正如您在上面写的那样,调用将起作用(您无需指定类型)。我猜这对您不起作用,因为您不能将属性用作“out”参数。解决方法是同时使用这两种方法:

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

        这将被称为:

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

        这是相当笨拙的,但不是更糟糕的方法。


        我在 C++ 中使用过但在 C# 中尚未尝试过的另一种方法是让 GetParameterValue() 一些您自己设计的对象,然后为它实现一些隐式强制转换运算符。

        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");
        

        【讨论】:

        • Marcus 在我阅读后编辑了他的答案。最初他有“GetParameterValue(out T dest)”,而不是“GetParameterValue(...)”。他编辑的答案是正确的。
        【解决方案7】:

        我认为当值为 null 时无法获取类型。此外,由于您在 GetParameterValue 内部调用,因此您能做的最好的事情(当值为 null 时)是获取“目标”参数的类型,即“对象”。您可能考虑将 Type 作为参数传递给 GetParameterValue 以获取更多信息,例如:

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

        【讨论】:

          【解决方案8】:

          如果没有实例,则没有实例类型。

          你能做的最好的就是使用引用的类型,这意味着如果你有一个对象引用(如问题中的方法),引用类型是对象。


          您可能不应该尝试将一种类型的空实例转换为另一种类型的空实例...

          【讨论】:

            【解决方案9】:

            在您的示例中,System.Object 类型为 null。

            您的示例甚至可以编译吗?我收到“无法从 'out string' 转换为 'out object'”错误。

            【讨论】:

            • 我的单元测试确实编译了,但我将实际的输出变量设置为一个对象而不是字符串。
            【解决方案10】:

            在理论上,null 与 C 中的 void 指针并不完全相同,也就是说它拥有一个内存地址,仅此而已?如果是这样,那么它类似于数学中除以零的情况,结果未定义。

            可以对这一行执行以下操作:

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

            只需删除第一个字符串,现在就没有重复了:

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

            不一定是您要查找的内容,尽管存在如何解释 null 的问题?

            【讨论】:

            • 问题不是“它们的 null 类型是什么”,而是“当前恰好设置为 null 的存储位置的类型是什么”所以“DataTable dt = null”,VM仍然知道 dt 是 DataTable 类型。
            • 在将变量设置为空时,它没有指向任何东西,这是我的观点。如果您有 3 个类,每个类都派生自另一个类,例如A 派生自 B,B 派生自 C,并且您有一个 B 类型的变量被分配为 null,您不能将 A 类型的变量分配给它吗?
            【解决方案11】:
            //**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);       
            }
            

            【讨论】:

              【解决方案12】:

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

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

              这样我们就可以使用 switch 来管理不同的属性类型了。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2014-01-07
                • 2020-10-23
                • 2010-11-28
                • 2021-08-30
                • 1970-01-01
                相关资源
                最近更新 更多