- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在研究本质上是文档解析和分析框架的架构。给定文档的行数,框架最终将生成一个表示文档的大对象(称为 Document
)。
管道中的早期过滤器需要逐行运行。但是,进一步向下的过滤器将需要转换(并最终生成)Document
对象。
为了实现这一点,我正在考虑使用这样的过滤器定义:
public interface IFilter<in TIn, out TOut> {
TOut Execute(TIn data);
}
所有过滤器都将在 PipelineManager
类中注册(与使用“链表”样式方法相反。)在执行之前,PipelineManager
将验证管道以确保没有过滤器被赋予错误的输入类型。
我的问题:拥有一个数据类型不断变化的管道在架构上是否合理(即一个好主意)?
附言我将我的应用程序实现为管道的原因是因为我觉得插件作者可以轻松替换/扩展现有过滤器。只需用不同的实现换出您想要更改的过滤器,就可以了。
最佳答案
编辑:请注意,已删除其他答案以替换为此 wall'o'text grin
NINJAEDIT:有趣的事实:Powershell(在@Loudenvier 的回答中提到)曾经被命名为“Monad”——另外,发现 Wes Dyer 的博客文章主题:The Marvels of Monads
查看整个“Monad”事物的一种非常非常简单的方法是将其视为具有非常基本界面的盒子:
使用在概念上同样简单 - 假设您有一个“东西”:
绑定(bind)位是真正有趣的部分,也是让大多数人脑袋爆炸的部分;基本上,你给出了如何将盒子链接在一起的各种规范:让我们来看一个相当简单的 Monad,“选项”或“也许”——有点像 Nullable<T>
,但更酷。
所以每个人都讨厌到处检查 null,但由于引用类型的工作方式,我们不得不这样做;我们喜欢是能够编写这样的代码:
var zipcodesNearby = order.Customer.Address.City.ZipCodes;
如果(客户有效 + 地址有效 + ...),要么返回有效答案,要么返回“无”,如果该逻辑的任何一点失败...但是不,我们需要:
List<string> zipcodesNearBy = new List<string>();
if(goodOrder.Customer != null)
{
if(goodOrder.Customer.Address != null)
{
if(goodOrder.Customer.Address.City != null)
{
if(goodOrder.Customer.Address.City.ZipCodes != null)
{
zipcodesNearBy = goodOrder.Customer.Address.City.ZipCodes;
}
else { /* do something else? throw? */ }
}
else { /* do something else? throw? */ }
}
else { /* do something else? throw? */ }
}
else { /* do something else? throw? */ }
(注意:如果适用,您也可以依赖空合并——尽管它看起来很讨厌)
List<string> nullCoalescingZips =
((((goodOrder ?? new Order())
.Customer ?? new Person())
.Address ?? new Address())
.City ?? new City())
.ZipCodes ?? new List<string>();
Maybe monad“规则”可能看起来有点像:
(注意:C# 不适合这种类型的类型处理,所以它有点不稳定)
public static Maybe<T> Return(T value)
{
return ReferenceEquals(value, null) ? Maybe<T>.Nothing : new Maybe<T>() { Value = value };
}
public static Maybe<U> Bind<U>(Maybe<T> me, Func<T, Maybe<U>> map)
{
return me != Maybe<T>.Nothing ?
// extract, map, and rebox
map(me.Value) :
// We have nothing, so we pass along nothing...
Maybe<U>.Nothing;
}
但这会导致一些讨厌的代码:
var result1 =
Maybe<string>.Bind(Maybe<string>.Return("hello"), hello =>
Maybe<string>.Bind(Maybe<string>.Return((string)null), doh =>
Maybe<string>.Bind(Maybe<string>.Return("world"), world =>
hello + doh + world).Value
).Value
);
幸运的是,有一个简洁的快捷方式:SelectMany
非常粗略地等同于“绑定(bind)”:
如果我们实现SelectMany
对于我们的 Maybe<T>
...
public class Maybe<T>
{
public static readonly Maybe<T> Nothing = new Maybe<T>();
private Maybe() {}
public T Value { get; private set;}
public Maybe(T value) { Value = value; }
}
public static class MaybeExt
{
public static bool IsNothing<T>(this Maybe<T> me)
{
return me == Maybe<T>.Nothing;
}
public static Maybe<T> May<T>(this T value)
{
return ReferenceEquals(value, null) ? Maybe<T>.Nothing : new Maybe<T>(value);
}
// Note: this is basically just "Bind"
public static Maybe<U> SelectMany<T,U>(this Maybe<T> me, Func<T, Maybe<U>> map)
{
return me != Maybe<T>.Nothing ?
// extract, map, and rebox
map(me.Value) :
// We have nothing, so we pass along nothing...
Maybe<U>.Nothing;
}
// This overload is the one that "turns on" query comprehension syntax...
public static Maybe<V> SelectMany<T,U,V>(this Maybe<T> me, Func<T, Maybe<U>> map, Func<T,U,V> selector)
{
return me.SelectMany(x => map(x).SelectMany(y => selector(x,y).May()));
}
}
现在我们可以利用 LINQ 理解语法了!
var result1 =
from hello in "Hello".May()
from oops in ((string)null).May()
from world in "world".May()
select hello + oops + world;
// prints "Was Nothing!"
Console.WriteLine(result1.IsNothing() ? "Was Nothing!" : result1.Value);
var result2 =
from hello in "Hello".May()
from space in " ".May()
from world in "world".May()
select hello + space + world;
// prints "Hello world"
Console.WriteLine(result2.IsNothing() ? "Was Nothing!" : result2.Value);
var goodOrder = new Order { Customer = new Person { Address = new Address { City = new City { ZipCodes = new List<string>{"90210"}}}}};
var badOrder = new Order { Customer = new Person { Address = null }};
var zipcodesNearby =
from ord in goodOrder.May()
from cust in ord.Customer.May()
from add in cust.Address.May()
from city in add.City.May()
from zip in city.ZipCodes.May()
select zip;
// prints "90210"
Console.WriteLine(zipcodesNearby.IsNothing() ? "Nothing!" : zipcodesNearby.Value.FirstOrDefault());
var badZipcodesNearby =
from ord in badOrder.May()
from cust in ord.Customer.May()
from add in cust.Address.May()
from city in add.City.May()
from zip in city.ZipCodes.May()
select zip;
// prints "Nothing!"
Console.WriteLine(badZipcodesNearby.IsNothing() ? "Nothing!" : badZipcodesNearby.Value.FirstOrDefault());
哈,我才意识到我忘了提及整个要点...所以基本上,一旦您弄清楚管道的每个阶段中“绑定(bind)”的等价物,您就可以使用相同类型的用于处理每个类型转换的包装、解包和处理的伪单子(monad)代码。
关于c# - 具有不断变化的数据类型的管道在架构上是否合理?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13770501/
我有一个曾经是 TreeView 控件的菜单,但现在我想让每个项目更加直观,并向树中的每个对象添加更多信息。 我的第一个意图是制作一个代表项目的用户控件,并在运行时将它们添加到面板中。这是一个好方法吗
我是 Docker 新手,想知道是否有可能(并且是一个好主意)在 Docker 容器中进行开发。我的意思是创建一个容器,执行 bash,安装和配置我需要的一切,然后开始在容器内进行开发。 容器将成为我
在 Java 中: Parent obj = new Child(); 我创建了一个 Parent 类型的对象。我假设我只能调用父类中定义的方法。因此,我无法调用 Child 中定义的“附加”方法或访
注意:我省略了其他两个阶段(V 和 W)的代码,示例中不需要。 我很确定,我这样处理“开”和“关”时间的方式并不是一种有效的方式。 我想使用查找表实现“开”和“关”脉动。计时器应与表的当前选定值进行比
当代码中包含 Java instanceof 运算符时,许多人会扬起眉毛并说这是禁忌。例如,在这个 other SO Q&A ,答案说: Note that if you have to use th
我是一名优秀的程序员,十分优秀!