动态类型如何作为通用类型使用?

11

我如何将动态类型用作泛型?

这个

var x = something not strongly typed;
callFunction<x>();

并且这个

dynamic x = something not strongly typed;
callFunction<x>();

两者都会产生这个错误

Error   1   The type or namespace name 'x' 
could not be found (are you missing a using directive or an assembly reference?)

我该怎么做才能使x合法并可以在<x>中使用?


2
你的代码很混乱。看起来你试图将一个变量作为类型参数传递?这样是行不通的。它必须是一个类型。 - M.Babcock
4
不是像 System.Type 那样的类型,而是直接像 stringint 那样的类型。变量不能用作类型参数。 - M.Babcock
“var x”并不意味着变量“x”没有强类型。编译器会推断(如果您愿意,也可以说是猜测)等号右侧表达式的类型,并将其作为“x”的类型。如果编译器认为表达式是“string”类型,则“var x”与“string x”完全相同。请注意,这发生在编译时而不是运行时! - Olivier Jacot-Descombes
你的示例代码没有意义:考虑一下如果x是强类型的:string x = "X";。你不会调用CallFunction<x>()或者CallFunction<typeof(string)>(); 你会调用CallFunction<string>()。这只有当CallFunction有一个字符串参数或返回值时才有意义;更合理的调用方式可能是 string x = CallFunction<string>(); 或者 CallFunction<string>(x)。你能给出一个你想调用的函数的例子吗?你想要实现什么目标? - phoog
@phoog - 没有尝试的余地;) 我确实在JonSkeet的完美帮助下实现了这一点。请参阅他的答案和下面的评论以获得更好的上下文。 - Travis J
显示剩余2条评论
6个回答

21
你可以使用类型推断来“反弹”调用:
dynamic x = something not strongly typed;
CallFunctionWithInference(x);

...

static void CallFunctionWithInference<T>(T ignored)
{
    CallFunction<T>();
}

static void CallFunction<T>()
{
    // This is the method we really wanted to call
}

这将根据x的执行时类型,在执行时确定类型参数,使用与x作为编译时类型时相同类型推断方法。该参数仅用于使类型推断起作用。

请注意,与Darin不同,我认为这是一种有用的技术 - 正是在预动态之前,您会通过反射调用通用方法的情况。您可以使代码中的一个部分动态化,但保持代码的其余部分(从通用类型以下)类型安全。它允许一个步骤是动态的 - 只有您不知道类型的单个位。


3
йӮЈжҳҜеӣ дёәCallFunctionжң¬иә«жңүйҷҗеҲ¶жқЎд»¶еҗ—пјҹдҪ еңЁй—®йўҳдёӯжІЎжңүе‘ҠиҜүжҲ‘们 :) еҰӮжһңжІЎжңүеҜ№CallFunctionиҝӣиЎҢйҷҗеҲ¶пјҢйӮЈд№Ҳе®ғеә”иҜҘжІЎй—®йўҳгҖӮ - Jon Skeet
我为这个答案设置了赏金,因为它太棒了。 - Travis J
我在使用Activator.CreateInstance尝试创建x时遇到了一些奇怪的行为。我已经苦苦挣扎了几天,希望您能提供一些见解?我得到了一个异常:外部:Object reference not set to an instance of an object. 内部:Microsoft.CSharp.RuntimeBinder.SymbolTable.GetOriginalTypeParameterType(Type t) +10。这是代码:dynamic AnyObject = Activator.CreateInstance("MyAssembly", "MyAssembly.Models.DbModels." + entityType).Unwrap();CallWithInference(AnyObject); 我做错了什么? - Travis J
1
@TravisJ:我认为如果您能创建一个新的问题并提供一个简短但完整的程序来展示问题,那么这将会有所帮助。 - Jon Skeet
我已经制作了一个完整的示例,但无法精确地复现问题。问题在这里:https://dev59.com/dGfWa4cB1Zd3GeqPi54B - Travis J
显示剩余4条评论

5

很难确定你尝试做什么。但是如果你想调用一个带有与某个对象相同的类型参数的通用方法,你不能直接这样做。但是你可以编写另一个方法,以你的对象作为参数,让 dynamic 推断类型,然后调用你想要的方法:

void HelperMethod<T>(T obj)
{
    CallFunction<T>();
}

…

dynamic x = …;
HelperMethod(x);

哇..这真是太棒了..微妙的差别在于,当 x 为空时,类型推断机制会失败。如果 x 是强类型的,则方法调用本身不会失败。只是需要记住这一点.. - nawfal

2
你无法使用泛型。泛型的整个意义在于编译时安全性,这意味着它们必须在编译时已知。而动态类型的整个意义在于你不需要在编译时知道确切的类型并使用运行时分派 => 它完全相反于泛型。因此不要浪费时间 => 一旦你进入动态/反射路径,你可以忘记泛型和编译时安全性。你将不得不走到底。

所以回答你的问题:

我能做什么来使x合法,以便在中使用?

唯一的事情是使用在编译时已知的类型,否则你不能使用泛型。


嗯,你可以使用泛型来处理在编译时未知的类型,可以看看我的回答。 - svick

1
你会收到这个错误是因为x不是一个类型。你需要将类型指定为类型参数。
实际上,如果使用正确,你可以使用dynamic作为类型参数:
var dict = new Dictionary<string, dynamic>();

dict.Add("Item1", 123);
dict.Add("Item2", "Blah");

这个编译并且运行得很好。


0

让这个工作最快的方法是将您的匿名类型转换为实际类型。

因此,不要使用

var x = new { Value = "somevalue", Text = "sometext" };

你需要做的是

class MyClass
{
    string Text, Value;
}
....
var x = new MyClass() { Value = "somevalue", Text = "sometext" };
//this should work now
callFunction<MyClass>(x);

请注意,在第一个代码片段中,x 仍然是强类型的;但是,它的类型是匿名的。 - Olivier Jacot-Descombes

0
你应该可以这样调用函数。
callFunction<dynamic>();

如果你的函数被定义为:
public void callFunction<T>(T arg) {
    ...
}

你可以直接调用它

callFunction(x);

C# 在许多情况下能够推断泛型类型参数。

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