我需要在 C# 中存根一個異步泛型方法。live 方法呼叫 REST API 并使用 Newtonsoft JSON 將結果反序列化為適當的格式,這些格式將是各種型別的串列。
然而,嘗試存根它會遇到各種問題。下面的主要代碼將失敗,因為它沒有回傳任務。
public Task<T> ExportReport<T>(string reportName)
{
if(reportName.Contains("personlookup "))
{
List<person> people = new();
people.Add(new person { Forenames = "Bob", Surname = "Brown" });
return people;
}
else if (reportName.Contains("GetOrder"))
{
List<order> orders = new();
orders.Add(new order { ItemType = “Apples”, ID = "1234" });
return orders;
}
else
{
return default;
}
}
我嘗試了各種方法,例如 Task.FromResult() 并將方法設定為 async 等,到目前為止唯一嘗試過甚至編譯的方法是
return (Task<T>)people.Cast<T>();
在運行時失敗并出現無效的強制轉換例外。我在這里做錯了什么,還是這種方法是不可能的。
uj5u.com熱心網友回復:
了解問題
問題不在于異步通用函式,而在于一般的通用函式。你有一個泛型函式,它回傳一個型別的物件,Task<T>其中T可以是任何型別。T 的型別由呼叫者決定,而不是被呼叫的方法。您正在嘗試存根此函式并回傳特定型別。想象一下,函式是這樣宣告的:
public T ExportReport<T>(string reportName)
由于相同的原因,您將面臨如何存根的相同問題。
測驗和重構
您存根的事實意味著您正在測驗(盡管您可能不是)。有時測驗會揭示我們代碼的設計缺陷。例如,在輸入引數(字串)上進行分支以回傳不同型別的物件的通用方法在這里可能不是最佳解決方案。
讓我們假設引數"a"回傳一個型別的物件A,引數"b"回傳一個型別的物件B。所以A myA = ExportReport("a")回傳一個A,但是當你輸入時會發生什么A myA = ExportReport("b")?因為它會編譯,你會在某處遇到問題。
通過使這個函式通用,你真的有什么成就嗎?
也許你應該從你的測驗經驗中得到這個提示并考慮重構?也許你可以有幾個不同的非泛型函式,每個函式回傳不同的報告型別?
討厭的解決方案(.NET5.0 或更高版本)
如果你不關心這些,只想讓它作業,你可以做這樣的事情:
public T ExportReport<T>(string reportName)
{
if (reportName.Contains("personlookup "))
{
List<Person> people = new();
people.Add(new Person { Forenames = "Bob", Surname = "Brown" });
return Unsafe.As<List<Person>,T>(ref people);
}
throw new ArgumentOutOfRangeException(nameof(reportName));
}
和`任務版本:
public Task<T> ExportReport<T>(string reportName)
{
if (reportName.Contains("personlookup "))
{
List<Person> people = new();
people.Add(new Person { Forenames = "Bob", Surname = "Brown" });
var peopleTask = Task.FromResult(people);
return Unsafe.As<Task<List<Person>>,Task<T>>(ref peopleTask);
}
throw new ArgumentOutOfRangeException(nameof(reportName));
}
更好的解決方案
聽起來您可以序列化存根資料并使用常規 JSON 轉換器對其進行消毒,這也解決了問題。
最佳解決方案
大概是重構。
uj5u.com熱心網友回復:
來自評論:
它不是用于單元測驗。我只是想將它與內部 API 服務器分離,以便我可以離線處理 UI 元素。
由于它是是有限目的話,我想你可以這樣做:
// Using `typeof(IEnumerable<T>)` instead of `typeof(List<T>)` or `typeof(IList<T>)` because it means `IsAssignableFrom` will work with `List<T>`, `T[]`, `IReadOnlyList<T>`, `IList<T>`, `ImmutableList<T>`, etc.
// It's maddening that even in .NET 6, `IList<T>` still does not extend `IReadOnlyList<T>`. Grumble.
private static readonly _typeofIEnumerablePerson = typeof(IEnumerable<Person>);
private static readonly _typeofIEnumerableOrder = typeof(IEnumerable<Order>);
public Task<T> ExportReportAsync<T>(string reportName)
{
T list = GetStubList<T>();
return Task.FromResult( list );
}
private static TList GetStubList<TList>()
{
if( _typeofIEnumerablePerson.IsAssignableFrom( typeof(TList) ) )
{
List<Person> people = new List<Person>()
{
new Person { Forenames = "Bob", Surname = "Brown" }
};
return ToTListWorkaround<TList>( people );
}
else if( _typeofIEnumerableOrder.IsAssignableFrom( typeof(TList) ) )
{
List<Order> orders = new List<Order>()
{
new Order { ItemType = "Apples", ID = "1234" }
};
return ToTListWorkaround<TList>( orders );
}
else
{
throw new NotSupportedException( "Unsupported list type: " typeof(TList).FullName );
}
}
private static TList ToTListWorkaround<TList>( IEnumerable actual )
{
// Ugly workaround:
Object asObject = actual;
TList returnValue = (TList)asObject;
return returnValue;
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/384273.html
上一篇:構造物件的通用方法
