![](/img/trans.png)
[英]How to pass the x in typeof(x) as a method parameter when x is not a variable but an enum type
[英]Pass typeof(x) type to generic method
我制作了一些中間件,可以記錄用戶在我的應用程序中執行的所有操作。 根據所采取的操作,我需要將一些[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 logAction方法
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返回null以外的任何內容。
唯一的例外是告訴您出了什么問題。
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
才能獲得其屬性(如您的問題所暗示)。 在那種情況下,無論如何您可能都不想要通用方法。
如果您使用的是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);
}
我能夠在重復帖子的幫助下獲得此信息。
在我的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});
}
}
Type(一個類)與泛型T之間是有區別的。您試圖從字典中獲取的T只是Type類的普通變量,而不是您可以作為泛型類型參數傳遞的任何內容。 您可能需要稍作更改,才能在不使用反射的情況下實現所需的功能。
方法1.讓LogAction將Type作為參數,並希望有一個接受此類參數的重載版本:
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);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.