- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我有一个以前从未遇到过的怪异的设计情况...如果使用的是Objective-C,我可以使用类别来解决它,但是我必须使用C#2.0。
首先,一些背景。我在这个类库中有两个抽象层。底层为扫描内容的组件实现了插件架构(抱歉,不能比这更具体)。每个插件都将以某种独特的方式进行扫描,但是插件也会因接受的内容类型而异。由于各种与本次讨论无关的原因,我不想通过插件界面公开泛型。因此,我最终得到了每种内容类型的IScanner接口(interface)和派生接口(interface)。
顶层是一个便利包装,它接受包含各个部分的复合内容格式。不同的扫描仪将需要复合 Material 的不同部分,具体取决于它们感兴趣的内容类型。因此,我需要具有特定于每个IScanner派生接口(interface)的逻辑来解析复合 Material 内容,以查找所需的相关部分。
解决此问题的一种方法是简单地向IScanner添加另一种方法,并在每个插件中实现它。但是,两层设计的重点是插件本身不需要了解复合格式。解决此问题的蛮力方法是在上层进行类型测试和下转换,但是随着将来增加对新内容类型的支持,这些内容将需要仔细维护。在这种情况下,访问者模式也会很尴尬,因为实际上只有一个访问者,但是不同的可访问类型的数量只会随着时间而增加(即,这是适合访问者的相反条件)。另外,当我真正想要的是劫持IScanner的单派调度时,双派调度感觉就像是过分杀了!
如果使用的是Objective-C,则只需在每个IScanner派生的接口(interface)上定义一个类别,然后在其中添加parseContent方法。类别将在上层定义,因此无需更改插件,同时避免了类型测试的需要。不幸的是,C#扩展方法因为基本上是静态的而无法工作(即-与调用站点上使用的引用的编译时类型相关联,而不是像Obj-C Categories那样挂接到动态分配中)。更不用说,我必须使用C#2.0,所以扩展方法甚至对我都不可用。 :-P
那么,有没有一种干净,简单的方法可以解决C#中的问题,类似于用Objective-C类别解决问题的方法呢?
编辑:一些伪代码可以帮助使当前设计的结构变得清晰:
interface IScanner
{ // Nothing to see here...
}
interface IContentTypeAScanner : IScanner
{
void ScanTypeA(TypeA content);
}
interface IContentTypeBScanner : IScanner
{
void ScanTypeB(TypeB content);
}
class CompositeScanner
{
private readonly IScanner realScanner;
// C-tor omitted for brevity... It takes an IScanner that was created
// from an assembly-qualified type name using dynamic type loading.
// NOTE: Composite is defined outside my code and completely outside my control.
public void ScanComposite(Composite c)
{
// Solution I would like (imaginary syntax borrowed from Obj-C):
// [realScanner parseAndScanContentFrom: c];
// where parseAndScanContentFrom: is defined in a category for each
// interface derived from IScanner.
// Solution I am stuck with for now:
if (realScanner is IContentTypeAScanner)
{
(realScanner as IContentTypeAScanner).ScanTypeA(this.parseTypeA(c));
}
else if (realScanner is IContentTypeBScanner)
{
(realScanner as IContentTypeBScanner).ScanTypeB(this.parseTypeB(c));
}
else
{
throw new SomeKindOfException();
}
}
// Private parsing methods omitted for brevity...
}
最佳答案
听起来您在说的是您的内容布局如下:
+--------+| part 1 || type A |+--------+| part 2 || type C |+--------+| part 3 || type F |+--------+| part 4 || type D |+--------+
and you have readers for each part type. That is, an AScanner knows how to process the data in a part of type A (such as part 1 above), a BScanner knows how to process the data in a part of type B, and so forth. Am I right so far?
Now, if I understand you, the problem that you're having is that the type readers (the IScanner
implementations) don't know how to locate the part(s) they recognize within your composite container.
Can your composite container correctly enumerate the separate parts (i.e., does it know where one part ends and another begins), and if so, does each part have some sort of identification that either the scanner or the container can differentiate?
What I mean is, is the data laid out something like this?
+-------------+| part 1 || length: 100 || type: "A" || data: ... |+-------------+| part 2 || length: 460 || type: "C" || data: ... |+-------------+| part 3 || length: 26 || type: "F" || data: ... |+-------------+| part 4 || length: 790 || type: "D" || data: ... |+-------------+
If your data layout is similar to this, could the scanners not request of the container all parts with an identifier matching a given pattern? Something like:
class Container : IContainer{
IEnumerable IContainer.GetParts(string type){
foreach(IPart part in internalPartsList)
if(part.TypeID == type)
yield return part;
}
}
class AScanner : IScanner{
void IScanner.ProcessContainer(IContainer c){
foreach(IPart part in c.GetParts("A"))
ProcessPart(part);
}
}
delegate void PartCallback(IPart part);
class Container : IContainer{
void IContainer.GetParts(PartCallback callback){
foreach(IPart part in internalPartsList)
callback(part);
}
}
class AScanner : IScanner{
void IScanner.ProcessContainer(IContainer c){
c.GetParts(delegate(IPart part){
if(IsTypeA(part))
ProcessPart(part);
});
}
bool IsTypeA(IPart part){
// determine if part is of type A
}
}
- The scanners should not have any knowledge of the container type.
- The container type has no built-in intelligence. It is as close to plain old data as you can get in C#.
- I can't change the container type; It is part of an existing architecture.
IScanner
接口(interface)不应该知道IContainer
接口(interface),以便将来有自由更改IContainer
接口(interface)的权限,则可以采用以下两种方法之一进行折衷:IPartProvider
接口(interface),该接口(interface)是IContainer
派生自(或包含)的。该IPartProvider
仅提供提供零件服务的功能,因此它应该非常稳定,并且可以在与IScanner
相同的程序集中定义,因此您的插件无需引用定义了IContainer
的程序集。 IScanner
除外)。 IScanner
派生形式,即定义了唯一扫描”方法,并且
CompositeScanner
类对每种零件类型都有唯一的“解析”方法。
IScanner
接口(interface)的实现者)与主应用程序分离,而我假设这是
CompositeScanner
类所在的地方。我较早的建议之一是如何实现该目标,但具体细节取决于
parseType
X函数的工作方式。这些可以抽象和概括吗?
parseType
X函数与
Composite
类对象进行通信以获得所需的数据。是否可以将它们移到通过
Parse
类代理的
IScanner
接口(interface)上的
CompositeScanner
方法中,以便从
Composite
对象获取此数据?像这样的东西:
delegate byte[] GetDataHandler(int offset, int length);
interface IScanner{
void Scan(byte[] data);
byte[] Parse(GetDataHandler getData);
}
class Composite{
public byte[] GetData(int offset, int length){/*...*/}
}
class CompositeScanner{}
IScanner realScanner;
public void ScanComposite(Composite c){
realScanner.Scan(realScanner.Parse(delegate(int offset, int length){
return c.GetData(offset, length);
});
}
}
Parse
上的单独
IScanner
方法并将简单的
GetDataHandler
委托(delegate)直接传递给
Scan
来简化此操作(如果需要,其实现可以调用私有(private)
Parse
)。然后,代码看起来与我之前的示例非常相似。
// parts identified by their offset within the file
class MainApp{
struct BlockBounds{
public int offset;
public int length;
public BlockBounds(int offset, int length){
this.offset = offset;
this.length = length;
}
}
Dictionary<Type, BlockBounds> plugins = new Dictionary<Type, BlockBounds>();
public void RegisterPlugin(Type type, int offset, int length){
plugins[type] = new BlockBounds(offset, length);
}
public void ScanContent(Container c){
foreach(KeyValuePair<Type, int> pair in plugins)
((IScanner)Activator.CreateInstance(pair.Key)).Scan(
c.GetData(pair.Value.offset, pair.Value.length);
}
}
// parts identified by name, block length stored within content (as in diagram above)
class MainApp{
Dictionary<string, Type> plugins = new Dictionary<string, Type>();
public void RegisterPlugin(Type type, string partID){
plugins[partID] = type;
}
public void ScanContent(Container c){
foreach(IPart part in c.GetParts()){
Type type;
if(plugins.TryGetValue(part.ID, out type))
((IScanner)Activator.CreateInstance(type)).Scan(part.Data);
}
}
}
Activator.CreateInstance
方法,那么最好不要使用
RegisterPlugin
。
关于c# - 有没有一种简单的方法可以在C#中模拟Objective-C类别?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/421119/
我遵循了一本名为“Sitepoint Full Stack Javascript with MEAN”的书中的教程,我刚刚完成了第 6 章,应该已经创建了一个带有“数据库”的“服务器”。数据库只不过是
在 Jquery 中,我创建两个数组,一个嵌入另一个数组,就像这样...... arrayOne = [{name:'a',value:1}, {name:'b',value:2}] var arra
这个问题在这里已经有了答案: What is the explanation for these bizarre JavaScript behaviours mentioned in the 'Wa
我被放在别人的代码上,有一个类用作其他组件的基础。当我尝试 ng serve --aot(或 build --prod)时,我得到以下信息。 @Component({ ...,
我正在测试一些代码,并使用数据创建了一个 json 文件。 问题是我在警报中收到“[object Object],[object Object]”。没有数据。 我做错了什么? 这是代码:
我想打印 [object Object],[object Object] 以明智地 "[[{ 'x': '1', 'y': '0' }, { 'x': '2', 'y': '1' }]]"; 在 ja
我有一个功能 View ,我正在尝试以特殊格式的方式输出。但我无法让列表功能正常工作。 我得到的唯一返回是[object Object][object Object] [object Object]
在使用优秀的 Sim.js 和 Three.js 库处理 WebGL 项目时,我偶然发现了下一个问题: 一路走来,它使用了 THREE.Ray 的下一个构造函数: var ray = new THRE
我正在使用 Material UI 进行多重选择。这是我的代码。 {listStates.map(col => (
我的代码使用ajax: $("#keyword").keyup(function() { var keyword = $("#keyword").val(); if (keyword.
我遇到了下一个错误,无法理解如何解决它。 Can't resolve all parameters for AuthenticationService: ([object Object], ?, [o
我正在尝试创建一个显示动态复选框的表单,至少应选中其中一个才能继续。我还需要获取一组选中的复选框。 这是组件的代码: import { Component, OnInit } from '@angul
我正在开发 NodeJs 应用程序,它是博客应用程序。我使用了快速验证器,我尝试在 UI 端使用快速闪存消息将帖子保存在数据库中之前使用闪存消息验证数据,我成功地将数据保存在数据库中,但在提交表单后消
我知道有些人问了同样的问题并得到了解答。我已经查看了所有这些,但仍然无法解决我的问题。我有一个 jquery snipet,它将值发送到处理程序,处理程序处理来自 JS 的值并将数据作为 JSON 数
我继承了一个非常草率的项目,我的任务是解释为什么它不好。我注意到他们在整个代码中都进行了这样的比较 (IQueryable).FirstOrDefault(x => x.Facility == fac
我只是在删除数组中的对象时偶然发现了这一点。 代码如下: friends = []; friends.push( { a: 'Nexus', b: 'Muffi
这两个代码片段有什么区别: object = nil; [object release] 对比 [object release]; object = nil; 哪个是最佳实践? 最佳答案 object
我应该为其他人将从中继承的第一个父对象传递哪个参数,哪个参数更有效 Object.create(Object.prototype) Object.create(Object) Object.creat
我在不同的对象上安排不同的选择器 [self performSelector:@selector(doSmth) withObject:objectA afterDelay:1]; [self per
NSLog(@"%p", &object); 和 NSLog(@"%p", object); 有什么区别? 两者似乎都打印出一个内存地址,但我不确定哪个是对象的实际内存地址。 最佳答案 这就是我喜欢的
我是一名优秀的程序员,十分优秀!