【问题标题】:How to cast an object to any type using object.GetType()如何使用 object.GetType() 将对象转换为任何类型
【发布时间】:2021-11-04 20:47:18
【问题描述】:

我有一个object 类型变量,我想将它转换为其原始数据类型。数据类型可以是任何类型(int、enum、class 等)

(object.GetType())object 不工作

已编辑:

我想再次将其转换为原始的原因,因为我正在构建一个带有多个参数的 cosmos db 查询

我有一个方法QueryCosmos(Dictionary<string, object> parameters),其中key是cosmos中的字段名,value是符合条件的字段的值(可以是任何数据类型)。

static string QueryCosmos(Dictionary<string, object> parameters)
{
   var sb = new StringBuilder($"SELECT * FROM c WHERE ");
   foreach (var p in parameters)
   {
      if (!(p.Value is string))
      {
         // this handles non-string data types
         var value = p.Value == null ? "null" : p.Value.ToString().ToLower();  
         sb.Append($"c.{p.Key} = {value} and ");
      }
      else
      {
         sb.Append($"c.{p.Key} = '{p.Value}' and ");
      }
   }
   return sb.ToString().Substring(0, sb.ToString().LastIndexOf(" and "));
}

我的问题是这行代码var value = p.Value == null ? "null" : p.Value.ToString().ToLower();

如果我传递 enum 类型,与 cosmos 中的数据不匹配。因为在cosmos中,这是存储为枚举的数值。我需要将其转换为 numeric 值。可以传递多种可能的enum 类型。这就是为什么我想要一种动态方式来转换为原始数据类型的原因

希望这能消除疑虑

【问题讨论】:

  • 您可能有 X/Y 问题。您永远不需要在 C#/.NET 中从 Object 向上转换。请发布与此问题相关的所有代码。
  • "我想将其转换为原始数据类型。" - 您为什么要这样做?您的程序将如何真正利用知道对象的运行时类型是什么?
  • 确定 cosmos 对参数有原生支持,无需将所有内容都分解为字符串。
  • @chary 您的 QueryCosmos 方法容易受到 SQL 注入的攻击。这是一个重大的安全问题。
  • @chary 我已经更新了我的答案,以便它处理Enum 值(包括enum 的所有大小)。

标签: c# reflection casting dynamic-cast targettype


【解决方案1】:

我没有查看您的新编辑,但希望我的回答仍然相关。

如果您希望将实例转换为其已知的具体类类型,则可以使用 as 运算符。

下面是一些示例代码:

using System.IO;
using System;
using System.Collections.Generic;

class Program
{
    public class BaseClass{
        public string Base="BaseClass";
    }
    public class Class1:BaseClass{
        public string Value="Class 1";
    }
    public class Class2:BaseClass{
        public string Value="Class 2";
        public string AnotherValue="Another value";
    }
    
    static void Main()
    {
        List<BaseClass> list=new List<BaseClass>(){new Class1(), new Class2()};
        
        foreach( var item in list)
        {
            Console.WriteLine($"Item.Base = {item.Base}");
            if(typeof(Class1).IsAssignableFrom( item.GetType() ))
            {
                Console.WriteLine($"Item.Value = {(item as Class1).Value}");
            }
            if(typeof(Class2).IsAssignableFrom( item.GetType() ))
            {
                Console.WriteLine($"Item.Value = {(item as Class2).Value}");
                Console.WriteLine($"Item.Value = {(item as Class2).AnotherValue}");
            }
        }
    }
}

这将为您提供以下输出:

Item.Base = BaseClass
Item.Value = Class 1
Item.Base = BaseClass
Item.Value = Class 2
Item.Value = Another value

【讨论】:

  • 考虑使用typeof(Class1).IsAssignableFrom( item.GetType() ) 而不是item.GetType() == typeof(Class1) - 这样您就可以处理非sealed 类型的层次结构。
  • @Dai - 很好,谢谢 - 我已经相应地更新了我的答案
【解决方案2】:

当您以 Azure CosmosDB 为目标时,您无需编写自己的查询生成器 the CosmosDB client library (Microsoft.Azure.Cosmos) has one built-in

在您的情况下,use QueryDefinition and WithParameter 构建强类型查询。 WithParameter 方法接受 object 形式的值,并为您处理特定于类型的逻辑。

当您使用动态查询时,下面的代码显示了如何安全地使用命名参数从字典中构建查询的 WHERE 子句。

永远不要将查询参数值直接嵌入到查询 SQL 本身中,因为这样做会导致 SQL 注入这是一件非常糟糕的事情


enum 更新:

我承认我不熟悉 Azure CosmosDB 客户端库,但我确实快速浏览了文档并在 ILSpy 中浏览了该库。

关于处理enum 值:QueryDefinition.WithParameter 方法应正确处理enum 值,因为它在其.Value 属性中保留了装箱的枚举值。

如果没有,则可以像这样将任何 enum 转换为 Int32 - 尽管使用拳击(虽然 可以投射 any enum to Int64 没有强制转换,需要运行时 IL 发射,这超出了这个答案的范围,but I have an implementation in my GitHub Gists 如果你有兴趣):

我已更新下面的代码以使用此 foreach 循环来检查 Enum(匹配 anyall enum 类型,无论它们的基础类型,尽管如果您有任何 enum : Int64 枚举的值超出 Int32 的范围,则此示例将失败 - 但这很容易修复,因此留给读者练习)。

    foreach( var kvp in parameters )
    {
        if( kvp.Value is Enum e )
        {
            Int32 enumAsInt32 = (Int32)Convert.ChangeType( e, TypeCode.Int32 );
            query = query.WithParameter( kvp.Key, enumAsInt32 );
        }
        else
        {
            query = query.WithParameter( kvp.Key, kvp.Value );
        }
    }

我原来的答案的其余部分:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

using Microsoft.Azure.Cosmos;
using Microsoft.Extensions.Configuration;

using Newtonsoft.Json;

//


public static QueryDefinition BuildCosmosQuery( Dictionary<String,Object> parameters )
{
    String sqlText = "SELECT * FROM foobar AS f WHERE " + GetWhereClauses( "f", parameters.Keys );

    QueryDefinition query = new QueryDefinition( sqlText );
    foreach( var kvp in parameters )
    {
        if( kvp.Value is Enum e )
        {
            Int32 enumAsInt32 = (Int32)Convert.ChangeType( e, TypeCode.Int32 );
            query = query.WithParameter( kvp.Key, enumAsInt32 );
        }
        else
        {
            query = query.WithParameter( kvp.Key, kvp.Value );
        }
    }
    
    return query;
}

private static String GetWhereClauses( String tableAlias, IEnumerable<String> parameterNames )
{
    return parameterNames
        .Select( pn => "{0}.{1} = @{1}".FmtInv( tableAlias, pn ) )
        .StringJoin( separator: " AND " );
}

//

public static class Extensions
{
    public static String StringJoin( this IEnumerable<String> source, String separator )
    {
        return String.Join( values: source, separator: separator ); 
    }

    public static String FmtInv( this String format, params Object[] args )
    {
        return String.Format( CultureInfo.InvariantCulture, format, args: args );
    }
}

See the QueryWithSqlParameters method in this code sample to see how to use QueryDefinition.

【讨论】:

    猜你喜欢
    • 2014-03-26
    • 2019-07-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-28
    • 1970-01-01
    • 2017-05-02
    相关资源
    最近更新 更多