代码之家  ›  专栏  ›  技术社区  ›  expenguin

将typeof(x)类型传递给泛型方法

  •  0
  • expenguin  · 技术社区  · 7 年前

    我制作了一些中间件,记录用户在我的应用程序中采取的所有操作。根据所采取的操作,我需要将一些[FromBody]JSON解析为它们各自的键/值对,以便进行日志记录。

    我需要在中间件中反序列化我的JSON,但是为了实现这一点,我需要将我的dtotype发送到反序列化器,以便它解析出我的键/值。我有一个方法设置来完成这项工作,但是我需要传递一个泛型类型,因为对于用户执行的每个操作,这都是不同的。(例如,我有一个userdto、customerdto等…)

    我已经设置了一个字典来获取我需要的类型,但是当我将var传递给我的日志方法来完成其余的工作时,我会得到一个错误,说明这不是一个类型,而是一个变量。这是真的,但是我不知道如何将我从字典中提取的类型转换为方法泛型类型。

    请参阅下面的代码:

    loggingmiddleware.cs只读字典

        private readonly Dictionary<string, Type> _postDictionary = new Dictionary<string, Type>
        {
            { "path/customers", typeof(CustomerPostDto) },
            ...//More entries//...
        };
    

    loggingmiddleware.cs调用方法

    public async Task Invoke(HttpContext context)
    {
        using (var streamCopy = new MemoryStream())
        {
            ...//Do some stuff here//...
            //Logging Actions
            if (request.Path != "/")
            {
                if (request.Method == "POST")
                {
                   Type T = _postDictionary[path];
                   logAction<T>(contextDto);
                }
            }
            ...//Do some stuff here//...
        }
    }
    

    loggingmiddleware.cs日志操作方法

    private void logAction<T>(object contextDto)
    {
        var dto = ControllerBase.ParseBody<T>(contextDto.Body);
        ...//Do some stuff here//...
    }
    

    编辑:以下示例可能重复-更新的代码

                    if (request.Method == "POST")
                    {
                        Type T = _postDictionary[path];
    
                        MethodInfo methodLogAction = typeof(LoggingMiddleware).GetMethod("logAction", BindingFlags.NonPublic);
                        MethodInfo generic = methodLogAction.MakeGenericMethod(T);
                        generic.Invoke(contextDto, null);
                    }
    

    上面的任何getmethod都不会返回空值以外的其他值。

    4 回复  |  直到 7 年前
        1
  •  1
  •   Mike Hofer    7 年前

    例外情况是告诉你到底出了什么问题。

    Type T = _postDictionary[path];
    

    这行代码 Type 实例,并将其存储在变量中, T . 然后,您尝试这样使用它:

    logAction<T>(contextDTO);
    

    但是,泛型方法需要在尖括号之间有一个非变量参数。类型在运行时不会更改;但是泛型方法的类型参数可以更改。(该语句有一些特定于编译器的细微差别,但现在我们将忽略这些细微差别。)

    你真正想要的是:

    logAction<SomeType>(contextDTO);
    

    但是如果您想将类型存储在 Dictionary ,您必须将该类型作为参数传递给方法,并失去通用功能:

    public void logAction(Type type, object data)
    {
        // Log the data here
    }; 
    

    这是因为 T 只有在运行时才知道,而不是在编译时。你得反思一下 T 获取它的属性(正如你的问题所暗示的那样)。在这种情况下,无论如何,您可能不需要一个泛型方法。

        2
  •  0
  •   3xGuy    7 年前

    如果你在使用 json.net 你可以这样做:

        public void LogAction<T>(string contextDto, Type type)
        {
            T obj = (T)JsonConvert.DeserializeObject(contextDto, type) ;
        }
    

    或者如果我读错了,而你和你想要这样的东西,你可以这样做。

        public void LogAction<T>(T obj)
        {
    
        }
        public ActionResult Test([FromBody] Thing thing)
        {
            LogAction(thing);
        }
    
        3
  •  0
  •   expenguin    7 年前

    我在复印件的帮助下得到了这个。

    在invoke方法中,我使用getmethod查找我的方法,并根据字典分配一个泛型类型。因为它是一个私有方法,所以我必须同时使用bindingFlags.nonpublic&bindingFlags.Instance标志才能找到该方法。

                //Logging Actions
                if (request.Path != "/")
                {
                    if (request.Method == "POST")
                    {
                        Type T = _postDictionary[path];
    
                        MethodInfo methodLogAction = typeof(LoggingMiddleware).GetMethod("LogAction", BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] {typeof(object)}, null);
                        MethodInfo generic = methodLogAction.MakeGenericMethod(T);
                        generic.Invoke(this, new object[]{contextDto});
                    }
                }
    
        4
  •  0
  •   Classe    7 年前

    类型(类)和泛型类型t之间存在差异。您试图从字典中获得的t只是类类型的一个普通变量,而不是作为泛型类型参数传递的任何内容。您可能需要稍微改变一下您的方法,以便在不使用反射的情况下实现您想要的。

    方法1。让logaction将一个类型作为参数,并希望有一个重载的版本接受这样的参数:

    private void LogAction(object contextDto, Type type) {
        ControllerBase.ParseBody(contextDto.Body, type);
    }
    

    或者您可以使用func更好地控制解析行为,比如

        // Method to get a Func that can parse your object
        private static Func<System.IO.Stream, T> GetParser<T>()
        {
            return (contextDto) => ControllerBase.ParseBody<T>(contextDto.Body);
        }
    
        // Use that in your dictionary
        private Dictionary<string, Func<System.IO.Stream, object>> transformers = new Dictionary<string, Func<System.IO.Stream, object>>
        {
            {  "/myPath", GetParser<CustomerPostDto>() },
            {  "/myPath-2", GetParser<CustomerPostDto>() }
        };
    
        // Now the LogAction method can just take the DTO that will be inferred from our parser
        private void LogAction<T>(T dto)
        {
            ...//Do some stuff here//...
        }
    
        // And call it as such
        if (transformers.ContainsKey(path))
                LogAction(transformers[path](context.Response.Body));
    

    我建议你不要过于深思熟虑,因为从长远来看,它会给你更多的控制权。

    通过分离日志记录和其他不相关的代码,您可以获得更多的乐趣和抽象:

        // Return a logger with a specification on how to parse a stream from a body
        private static TypeLogger CreateLogger<T>()
        {
            return new TypeLogger<T>((ctx) => ControllerBase.ParseBody<T>(contextDto.Body));
        }
    
        // Create a list with paths and loggers of specified type
        private Dictionary<string, TypeLogger> loggers = new Dictionary<string, TypeLogger>
        {
            { "/path1", CreateLogger<CustomerPostDto>() },
            { "/path2", CreateLogger<CustomerPostDto>() },
        };
    
        // Abstract base logger class that allows you to log from a requestbody
        public abstract class TypeLogger
        {
            public abstract void Log(System.IO.Stream requestBody);
        }
    
        // An actual implementation to parse your dto using by using the parser previously specified
        public class TypeLogger<T> : TypeLogger
        {
            // Parser to use when getting entity
            public Func<System.IO.Stream, T> Parser { get; private set; }
    
            // Constructor that takes sa func which helps us parse
            public TypeLogger(Func<System.IO.Stream, T> parser) => Parser = parser;
    
            // The actual logging
            public override void Log(System.IO.Stream requestBody)
            {
                var dto = Parser(requestBody);
    
                //...
            }
        }
    
        // And usage
        if (loggers.Contains(path))
            loggers[path].Log(ctx.Response.Body);