Is there an easy way to imitate Objective-C categories in C #?

I have a strange design situation that I have never encountered before ... If I used Objective-C, I would solve it using categories, but I need to use C # 2.0.

Firstly, some background. I have two layers of abstraction in this class library. The lower level implements a plug-in architecture for components that scan content (sorry, could not be more specific than that). Each plugin will perform its own scanning in some unique way, but plugins can vary depending on what type of content they accept. I did not want to reveal Generics through the plug-in interface for various reasons not related to this discussion. So, I got an IScanner interface and a derived interface for each type of content.

The top level is a convenient shell that accepts a composite content format that contains various parts. Different scanners will require different parts of the composite, depending on what type of content they are interested in. Therefore, I need to have a logic specific to each interface based on IScanner, which analyzes the composite content, looking for the corresponding required part.

One way to solve this problem is to simply add another method to IScanner and implement it in each plug-in. However, the whole point of the two-layer design is that the plug-ins themselves do not need to know about the composite format. Most likely, the solution to this problem is that type tests and downgrades are performed at the top level, but they must be carefully maintained, as support for new types of content is added in the future. The visitor pattern would also be inconvenient in this situation, because in fact there is only one visitor, but the number of different types of Visiting will only increase with time (i.e. these are the opposite conditions for which the visitor is suitable). Plus, dual dispatching seems redundant when really all I want is to capture a separate IScanner submission!

If I were using Objective-C, I would simply define a category for each interface based on IScanner, and add the parseContent method there. The category will be defined at the top level, so the plugins will not need to change, while avoiding the need for type tests. Unfortunately, C # extension methods will not work because they are mostly static (i.e. Bound to the compilation time type of the link used on the call site, and not connected to dynamic submission, such as Obj-C categories). Not to mention, I have to use C # 2.0, so extension methods are not even available to me. :-P

So, is there a simple and easy way to solve this problem in C #, similar to how this can be solved using Objective-C categories?


EDIT: some pseudo code to help make the structure of the current project understandable:

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... } 

EDIT: To clarify, I already thought a lot about this design. I have many reasons, most of which I cannot separate, because it is. I have not accepted any answers yet, because although they are interesting, they are shying away from the original question.

The fact is that in Obj-C I could solve this problem simply and elegantly. The question is, can I use the same technique in C #, and if so, how? I am not against looking for alternatives, but to be honest, this is not the question I asked. :)

+6
c # oop design-patterns objective-c visitor
source share
2 answers

It looks like you are saying that you have content that goes something like this:

  + -------- +
 |  part 1 |
 |  type A |
 + -------- +
 |  part 2 |
 |  type C |
 + -------- +
 |  part 3 |
 |  type F |
 + -------- +
 |  part 4 |
 |  type D |
 + -------- +

and you have readers for each type of detail. That is, AScanner knows how to process data in type A part (for example, part 1 above), BScanner knows how to process data in type B part, and so on. Am i still right?

Now, if I understand you, the problem you are facing is that type readers ( IScanner implementations) do not know how to find the part (s) that they recognize in your composite container.

Can your composite container correctly list the individual parts (i.e. does it know where one part ends and the other begins), and if so, does each part have some kind of identification that the scanner or container can distinguish?

What I mean is 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, can scanners not ask the container for all parts with an identifier that matches this template? 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); } } 

Or, if possible, the container will not be able to recognize the type of part, but the scanner will be able to recognize its own type of part, perhaps something like:

 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 } } 

Perhaps I misunderstood your content and / or your architecture. If so, check and I will update.


Comment by OP:

  • Scanners do not need to know the type of container.
  • The type of container does not have built-in intelligence. It is as close to old data as possible since you can get into C #.
  • I cannot change the type of container; This is part of the existing architecture.

My answers are too long for comments:

  • Scanners must have some way of extracting the part (s) they process. If you are concerned that the IScanner interface IScanner not need to know the IContainer interface IContainer that you can change the IContainer interface in the future, then you can compromise in one of two ways:

    • You can pass the scanners the IPartProvider interface, which the IContainer derived from (or contains). This IPartProvider will provide only the functionality of the servicing parts, so it should be quite stable, and it can be defined in the same assembly as IScanner , so your plugins will not need to reference the assembly where IContainer .
    • You can delegate to scanners that they can use to extract parts. Scanners then would not need knowledge of any interface (except IScanner , of course), only a delegate.
  • and

  • It looks like you need a surrogate class that knows how to communicate with both the container and the scanners. Any of the functions mentioned above can be implemented in any ol class if the container already provides public access (or securely [is this a word?]) So that an external / derived class can access the corresponding data.

From your pseudo-code in your edited question, it seems that you really do not get any advantages from the interfaces and tightly connect your plugins with your main application, since each type of scanner has a unique IScanner output that defines a unique “scan” method, and the class CompositeScanner has a unique "parse" method for each type of part.

I would say that this is your main problem. You need to separate the plugins that I believe are the developers of the IScanner interface — from the main application, which I assume lives in the CompositeScanner class. One of my previous suggestions is how I will implement this, but the exact details depend on how your parseType X functions parseType . Is it possible to abstract and generalize them?

Presumably, your parseType X functions interact with an object of the Composite class to get the data they need. Can they not be transferred to the Parse method on the IScanner interface, which is proxied through the CompositeScanner class, to get this data from the Composite object? Something like that:

 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); }); } } 

Of course, this could be simplified by removing a separate Parse method on IScanner and simply passing the GetDataHandler delegate directly to Scan (the implementation of which could call a private Parse , if necessary). The code looks very similar to my previous examples.

This design provides the greatest possible separation of problems and solutions that I can think of.


I just thought of something else that you might find more acceptable and, indeed, can provide a better separation of concerns.

If you can have each "register" plug-in with the application, you can leave the parsing in the application if the plug-in can tell the application how to extract its data. The following are examples, but since I don’t know how your parts are identified, I implemented two possibilities: one for indexed parts and one for named parts:

 // 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); } } 

or

 // 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); } } } 

Obviously, I have greatly simplified these examples, but I hope you understand this idea. Also, instead of using Activator.CreateInstance it would be nice if you could pass the factory (or factory delegate) to the RegisterPlugin method.

+1
source share

I'm going to try ... ;-) If there is a phase in your system when you fill out your "catalog" of IScanner objects, you might think about decorating your IScanner attribute, indicating which Part they are interested in. You can then map this information and disk scan of your Composite using a map. This is not a complete answer: if I have some time, I will try to clarify ...

Edit: a bit of pseudo code to support my confusing explanation

 public interface IScanner { void Scan(IPart part); } public interface IPart { string ID { get; } } [ScannedPart("your-id-for-A")] public class AlphaScanner : IScanner { public void Scan(IPart part) { throw new NotImplementedException(); } } [ScannedPart("your-id-for-B")] public class BetaScanner : IScanner { public void Scan(IPart part) { throw new NotImplementedException(); } } public interface IComposite { List<IPart> Parts { get; } } public class ScannerDriver { public Dictionary<string, IScanner> Scanners { get; private set; } public void DoScan(IComposite composite) { foreach (IPart part in composite.Parts) { IScanner scanner = Scanners[part.ID]; scanner.Scan(part); } } } 

Do not take it as it is: it is for explaining purpose.

Edit: Respond to Colonel Kernel’s comments. I am glad that you find this interesting. :-) In this simple sketch, you need to use code reflections only during the initialization of the dictionary (or when it is necessary), and at this point you can "ensure" the presence of the attribute (or even use other matching methods scanners and parts). I say “forced” because even if it’s not a compilation time limit, I think you will run your code at least once before putting it into production ;-) so it can be a run-time limit if necessary. I would say that inspiration is something (very very easy) that resembles MEF or other similar frameworks. Only my 2 cents.

+1
source share

All Articles