C#类型参数作为泛型声明

5

我在尝试为一个泛型方法指定类型参数时遇到了错误。

错误:'JsonFilter.JsonDataType' 是一个 '属性',但是像 '类型' 一样使用

public class JsonFilter : ActionFilterAttribute
{
    public Type JsonDataType { get; set; }
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        ...
        JavaScriptSerializer jss = new JavaScriptSerializer();
        var result = jss.Deserialize<JsonDataType>(inputContent);//Error here
        ...

新代码

...
JavaScriptSerializer jss = new JavaScriptSerializer();
MethodInfo method = jss.GetType()
               .GetMethod("Deserialize")
               .MakeGenericMethod(new Type[] { JsonDataType });
var result = method.Invoke(jss, new object[] { inputContent });
filterContext.ActionParameters[Param] = result;
...

反射拯救了这一天。感谢@Jason的解释,当类型作为泛型方法的一部分指定(<类型名>)时,它会被编译成字节码。而当作为属性时,它可以是任何类型,在运行时确定。

更新

针对这个特定问题,以下代码更为简洁。

var o = new DataContractJsonSerializer(JsonDataType).ReadObject(
    filterContext.HttpContext.Request.InputStream);
filterContext.ActionParameters[Param] = o;

我可能完全错了,但是看起来你想让Generic Deserialize<T>方法与在运行时定义的Type一起工作... 我不认为泛型可以这样工作 - 它们在设计时很好,而且“通用”,但它们编译成特定于类型的代码... 真糟糕。我的理解不足表现出来了。底线:我不认为你试图做的事情是可能的。你不能使用泛型与运行时分配的类型。 - Tao
@Tao:可以使用反射来完成。在正确的情况下,这实际上是一种非常强大的技术。 - jason
4个回答

14

这个错误

错误: 'JsonFilter.JsonDataType' 是一个 '属性',但被用作 '类型'

恰好告诉了你问题所在。

var result = jss.Deserialize<JsonDataType>(inputContent);

在这里,你试图将 JsonDataType 作为类型参数传递给泛型方法 JavaScriptSerializer.Deserialize<T>

但是在这里

public Type JsonDataType { get; set; }

您将JsonDataType声明为Type类型的属性,而不是类型本身。要使用通用方法,您需要传递一个类型参数(或者在某些情况下,让编译器推断一个类型)。例如:

var result = jss.Deserialize<string>(inputContent);

由于string是一种类型,因此使用它作为正确的用法。

现在,如果你非常想要使用由JsonDataType表示的类型,你可以使用反射。

MethodInfo generic = typeof(JavaScriptSerializer).GetMethod("Deserialize")
                                                 .GetGenericMethodDefinition();
MethodInfo closed = generic.MakeGenericMethod(new [] { JsonDataType });
closed.Invoke(jss, new object[] { inputContent });

你好,我在以下代码行收到了“找到模糊匹配项”异常:typeof(JavaScriptSerializer).GetMethod("Deserialize") .GetGenericMethodDefinition();你有任何想法为什么会出现这个错误吗?:( - Genady Sergeev

5

由于你不能向类中添加通用类型参数,因此你需要使用反射:

public class JsonFilter : ActionFilterAttribute
{
    public Type JsonDataType { get; set; }

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        // ...
        JavaScriptSerializer jss = new JavaScriptSerializer();

        MethodInfo method = jss.GetType()
                               .GetMethod("Deserialize");
                               .MakeGenericMethod(new Type[] { JsonDataType });
        var result = method.Invoke(jss, new object[] { inputContent });
        //...
    }
}

Corrected answer...

你能否使用一个通用类型参数代替JsonDataType属性?

public class JsonFilter<TJsonData> : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        // ...
        JavaScriptSerializer jss = new JavaScriptSerializer();
        var result = jss.Deserialize<TJsonData>(inputContent);
        //...
    }
}


“通用类型无法从 'ActionFilterAttribute' 派生,因为它是一个属性类”... 无法使属性成为通用的。 - oglester
@Greg,如果是这样的话,我想使用反射方式更新答案就是正确的做法了。 - LukeH
是的。虽然,正如您可能知道的那样,在其他情况下,您最初提供的内容显然更好。 - oglester

2
尝试按照这个思路做。我会在稍后为您的代码进行更新。
typeof(IDependencyResolver).GetMethod("Resolve", new Type[] { })
                .MakeGenericMethod(type)
                .Invoke(this, null);

这里有一份初稿给您参考。
typeof(JavaScriptSerializer).GetMethod("Deserialize", new Type[] {} /* param types */)
    .MakeGenericMethod(JsonDataType).Invoke(jss, /*params*/);

基本上,它使用反射机制来调用通用方法。

-1

你不能以这种方式传递泛型类型参数 - 尝试像这样:

public class JsonFilter<T> : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        JavaScriptSerializer jss = new JavaScriptSerializer();
        var result = jss.Deserialize<T>(inputContent);//Error here
    }
}

不要在属性中设置类型,而是将您的JsonFilter类设为泛型,以便您可以将泛型类型参数传递给JavaScriptSerializer


2
“通用类型无法从 'ActionFilterAttribute' 派生,因为它是一个属性类”... 无法使属性成为通用的。 - oglester

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