在Scala中,您可以使用模式匹配根據輸入的類型生成結果。例如:在C#中實現模式匹配
val title = content match {
case blogPost: BlogPost => blogPost.blog.title + ": " + blogPost.title
case blog: Blog => blog.title
}
在C#中,我非常希望能夠寫:
var title = Visit(content,
(BlogPost blogPost) => blogPost.Blog.Title + ": " + blogPost.Title,
(Blog blog) => blog.Title
);
這可能嗎?當我嘗試將其作爲單一方法編寫時,我不知道如何指定泛型。下面的實現似乎是正確的,除了讓類型檢查,以允許接受的T亞型功能:
public TResult Visit<T, TResult>(T value, params Func<T, TResult>[] visitors)
{
foreach (var visitor in visitors)
{
if (visitor.Method.GetGenericArguments()[0].IsAssignableFrom(value.GetType()))
{
return visitor(value);
}
}
throw new ApplicationException("No match");
}
我已經得到最接近的是將功能添加到一個單獨的對象,然後調用參觀一值:
public class Visitor<T, TResult>
{
private class Result
{
public bool HasResult;
public TResult ResultValue;
}
private readonly IList<Func<T, Result>> m_Visitors = new List<Func<T, Result>>();
public TResult Visit(T value)
{
foreach (var visitor in m_Visitors)
{
var result = visitor(value);
if (result.HasResult)
{
return result.ResultValue;
}
}
throw new ApplicationException("No match");
}
public Visitor<T, TResult> Add<TIn>(Func<TIn, TResult> visitor) where TIn : T
{
m_Visitors.Add(value =>
{
if (value is TIn)
{
return new Result { HasResult = true, ResultValue = visitor((TIn)value) };
}
return new Result { HasResult = false };
});
return this;
}
}
這可以用於像這樣:
var title = new Visitor<IContent, string>()
.Add((BlogPost blogPost) => blogPost.Blog.Title + ": " + blogPost.Title)
.Add((Blog blog) => blog.Title)
.Visit(content);
不知道如何用一個方法調用做到這一點?
還挺看起來像一本字典,其中的關鍵是類型和值是一個函數... – Roly 2011-05-17 13:43:47
您正在使用C#3或4?在C#4中,Func類型在其正式參數類型中是相反的,這爲您提供了更多的轉換靈活性。 – 2011-05-17 14:09:10
@Eric Lippert:在這種情況下,我*想*我實際上想要協變而不是反轉。我想接受可能無法接受類型T參數的函數(而您通常會接受任何接受類型T參數的函數,其中包含接受U類參數的函數,其中T <:U) – 2011-05-17 14:20:36