如何在运行时生成一个未知类型的实例?

17

我在 C# 中有以下代码:

string typename = "System.Int32";
string value = "4";
这两个字符串应该被使用来生成一个指定类型和指定值的对象...
结果应该是:
object o = CreateUnknownType(typename, value);
...
Int32 test = (Int32)o;

4
你的问题是什么?为什么要创建一个未知类型,然后再将其转换为已知类型? - Lazarus
6
问题是:如何在运行时生成一个未知类型的实例?无论原因是什么,我只是想知道是否可能。 - haze4real
2
那很容易,是的,这是可能的。可行吗?可能不太现实。当你的例子没有意义时,要求一些上下文是不合理的吗? - Lazarus
8个回答

20

你是这样想的吗?

object result = Convert.ChangeType("4", Type.GetType("System.Int32"));

18

如上所述,这个问题过于宽泛,无法一般性地解决。

以下是一些选项:

Type type = Type.GetType(typename);
object o = Activator.CreateInstance(type);

这将创建一个描述为typename的类型的实例。 它调用该类型的无参构造函数。(缺点:并非所有对象都有无参构造函数。 此外,这确实使用value设置了对象的状态。)

Type type = Type.GetType(typename);
object o = Activator.CreateInstance(type, new[] { value });

这将创建一个由typename所描述的类型的实例。它调用该类型的一个接受string类型参数的构造函数。(缺点:并非所有对象都有这样的构造函数。例如,Int32没有这样的构造函数,因此您将遇到运行时异常。)

Type type = Type.GetType(typename);
object o = Convert.ChangeType(value, type);

这将尝试将字符串value转换为所需类型的实例。但这可能导致InvalidCastException异常。例如,Convert.ChangeType("4", typeof(FileStream))显然会失败,就像它应该做的那样。

事实上,最后一个示例(创建一个类型为FileStream的实例,其初始状态由字符串"4"确定)显示了一般问题的荒谬性。有些构造/转换是无法完成的。

你可能需要重新考虑你要解决的问题,以避免这种混乱局面。


3

通过已知名称创建一个类型的实例(该类型应该具有默认构造函数):

   string typeName = "System.Int32";
   Type type = Type.GetType(type);
   object o = Activator.CreateInstance(type);

从字符串中解析值显然只适用于有限的一些类型。您可以使用以下方法:
  • use Convert.ChangeType as suggested by PhilipW
  • or maybe create a Dictionary<Type,Func<string,object>> which maps known types to known parse functions
  • or use reflection to invoke the Parse(string) method on the type, assuming there is one:

       string valueText = "4";
       MethodInfo parseMethod = type.GetMethod("Parse");
       object value = parseMethod.Invoke(null, new object[] { valueText });
    
  • or maybe you can use the infrastructure provided by the .NET component model. You can fetch the type converter of a component and use it like this:

       TypeConverter converter = TypeDescriptor.GetConverter(type);
       object value = converter.ConvertFromString(valueText);
    

1

你的逻辑似乎有点错误。显然,如果你稍后直接将对象转换为它实际的类型,那么你必须一开始就知道这个类型。

如果这个问题还有其他缺失,请详细说明,也许有比“这没有多大意义”更合适的答案。


1

也许你有一组不同类型的对象,它们都实现了一个已知的接口?

例如,如果你有几个不同的用户控件,并且想要将其中一个加载到容器中,每个控件可能都实现了IMyWobblyControl(一个已知的接口),但是在运行时可能不知道要加载哪个控件,可能需要从某种配置文件中读取字符串。

在这种情况下,您需要使用反射从类似完整程序集名称的内容中加载实际类型,然后将其强制转换为已知类型以使用它。

当然,您需要确保您的代码处理无效的转换、找不到程序集和其他异常,因为这些问题很可能会出现在这种不稳定的情况下...


0

使用后:

Type type = Type.GetType(typename);

尝试使用这个扩展方法:

public static class ReflectionExtensions
{
    public static T CreateInstance<T>(this Type source, params object[] objects)
        where T : class
    {            
        var cons = source.GetConstructor(objects.Select(x => x.GetType()).ToArray());
        return cons == null ? null : (T)cons.Invoke(objects);
    }
}

希望这可以帮到你。

0

这似乎是 Int32.Parse(string) 的工作。但为了与其他人达成一致,似乎这是“独特”的,所以可能应该考虑手套。


0

这里是一个关于Azure SQL Federations的问题的具体示例...它将数据根据密钥范围拆分为不同的数据库。

密钥范围类型如下:

SQL / .Net SQL type / CLR .Net

INT / SqlInt32 / Int32,Nullable

BIGINT / SqlInt64 / Int64,Nullable

UNIQUEIDENTIFIER / SqlGuid / Guid,Nullable

VARBINARY(n),最大n 900 / SqlBytes、SqlBinary / Byte[]

理想情况下,C#函数参数可以采用.NET SQL类型或CLR .NET类型,但仅选择一种类型类别也可以。

使用“对象”类型参数是否是正确的方式?而且,有没有可行的方法来识别类型并相应地进行转换?

大致的概念如下:

public void fn(object obj, string fedName, string distName, bool filteringOn)

{

...找出 obj 的类型,确保它是可接受的类型之一...

string key = obj.toString();

return string.Format("使用 FEDERATION {0} ({1}='{2}') WITH RESET, FILTERING = {3}", fedName, distName, key, (filteringOn ? "ON" : "OFF"));

}

尽管参数值被转换为字符串,但它将在 SQL 服务器端重新转换/检查,因此在应用程序端进行验证是必要的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接