最近我遇到了一个类似的问题,但是提出的解决方案都不太令人满意;限制类型参数也不实际。相反,我让方法的使用者决定如何处理数据。例如,你可以编写一个泛型版本的String.Split(),它返回一个强类型的List,只要你告诉它如何将子字符串转换为T类型。
一旦你愿意将责任转移至调用堆栈上(并且习惯于传递lambda表达式),你可以任意地推广这种模式。例如,如果你的GetData()方式有所变化(正如一些响应所假定的那样),你也可以将该函数提升到调用者的范围内。
演示:
static void Main(string[] args)
{
var parseMe = "Hello world! 1, 2, 3, DEADBEEF";
List<string> sentences = parseMe.Split('!', str => str);
List<int> numbers = sentences[1].Split(',', str => Int32.Parse(str, NumberStyles.AllowHexSpecifier | NumberStyles.AllowLeadingWhite));
var lettersPerSentence = Process(sentences,
sList => from s in sList select s.ToCharArray(),
chars => chars.Count(c => Char.IsLetter(c)));
}
static List<T> Split<T>(this string str, char separator, Func<string, T> Convert)
{
return Process(str, s => s.Split(separator), Convert).ToList();
}
static IEnumerable<TOutput> Process<TInput, TData, TOutput>(TInput input, Func<TInput, IEnumerable<TData>> GetData, Func<TData, TOutput> Convert)
{
return from datum in GetData(input)
select Convert(datum);
}
函数式编程专家可能会对这个探索感到厌倦:“你只是组合了几次 Map。” 即使 C++ 程序员可能会认为这是一个例子,其中模板技术(即 STL transform() + 函数对象)比泛型需要更少的工作。但作为一个主要使用 C# 的人,找到一种既保留类型安全性又符合惯用语言用法的解决方案还是很不错的。