English 中文(简体)
c# - How to do MULTIPLE "mixins" correctly with Interfaces and/or Abstract Classes
原标题:

I want to be able to define some objects and attach some "behaviors" to that object where the implementation is in the behavior not in the object. Rails-like: acts_as_taggable. As a concrete example, I want to say that Tasks can be Tagged. I don t want to have to code anything in Task about Tags beyond "enabling" the behavior via ... an interface? Therein lies my question. You can t put the implementation in an interface. I don t want to pollute my BaseObject [abstract?] class with all of the possible implementations.

Objects: Task, Note

Behaviors: Taggable, Emailable, Printable, Deferrable (

A Task may be tagged, emailed, printed, and deferred. A Note may be tagged, emailed, printed, but not deferred.

baseobject

public class BaseObject
{
    Guid ID { get; set; }
}

tag.cs

public class Tag : BaseObject
{
    public Guid Id { get; set; }
    public String Title { get; set; }
}

itaggable.cs

public interface ITaggable 
{
    void AddTag(Tag tag);
    ... other Tag methods ... 
}

task.cs

public class Task : BaseObject, ITaggable, IEmailable, IPrintable
{
    Task specified functionality... nothing about "taggging"
}

note.cs

...

TagCollection.cs

public class TagCollection : List<Tag>
{
    public string[] ToStringArray()
    {
        string[] s = new string[this.Count];
        for (int i = 0; i < this.Count; i++)
            s[i] = this[i].TagName;
        return s;
    }

    public override string ToString()
    {
        return String.Join(",", this.ToStringArray());
    }

    public void Add(string tagName)
    {
        this.Add(new Tag(tagName));
    }

Implementation of ITaggable looks something like

{
    private TagCollection _tc;
    private TagCollection tc
    {
        get
        {
            if (null == _tc)
            {
                _tc = new TagCollection();
            }
            return _tc;
        }
        set { _tc = value; }
    }

    public void AddTag(Tag tag)
    {
        tc.Add(tag);
    }

    public void AddTags(TagCollection tags)
    {
        tc.AddRange(tags);
    }

    public TagCollection GetTags()
    {
        return tc;
    }
}

So what s the correct/best way to do this?

Jason

问题回答

Well there are lots of ways depending on how you want to implement. In your example it might be better to do something along the lines of this:

public interface IBehavior
{
    ... common behavior methods like maybe
    bool Execute(object value)
}

public class Taggable : IBehavior
{
   ... tag specific items
   public bool Execute(object value) { ... }
}

public class Note
{
   public List<IBehavior> Behaviors { get; set; }
   public void ProcessNote()
   {
       this.Behaviors(d=>d.Execute(this));
   }
}

This variation would allow you to always add more behaviors without having to do any drastic changes to your class structures either like implementing an interface on every class that you want to support the new behavior. You may want to come up with what your common object would be amongst your behavior class to make it easier to use for your scenario though. So you could use generics to allow you to do a more typed definition.

You might want to look at the Decorator pattern as well to give you even more ideas.

Unfortunately you re going to have to add some code to your classes, either the base class or the descendant.

You can, however, get away with the "taggable" class being a private member, and just passing on all method calls to that member object, but you still need to implement the interface.

Something like this:

interface ITaggable {
    void AddTag(String tag);
}

class TaggableImplementation : ITaggable {
    private Hashset<String> tags = new Hashset<String>();
    public void AddTag(String tag) { tags.Add(tag); }
}

class TaggableClass : ITaggable {
    private ITaggable taggable = new TaggableImplementation();

    public void AddTag(String tag) { taggable.AddTag(tag); }
}

To create mixins using Visual Studio s built-in T4 code-generation technique, you may take a look at http://code.logos.com/blog/2009/04/creating_mixins_with_t4_in_visual_studio.html

I ve implemented mixins in C# by using partial classes, and copying source code from a template file into partial class source files. If you accept a few limitations, it works quite well. I tried the T4 pre-processor to automatically generate the source files, but it was a little too cumbersome, so I ve written a - quite minimalistic - tool to copy code from template files that are much easier to generate than T4 scripts. With a little care, you can even use Intellisense when writing your template.

For the tool, have a look at:

http://myxin.codeplex.com/

For an example, have a look at the source code for Streambolics.Gui at:

http://streambolicscore.codeplex.com/





相关问题
Anyone feel like passing it forward?

I m the only developer in my company, and am getting along well as an autodidact, but I know I m missing out on the education one gets from working with and having code reviewed by more senior devs. ...

NSArray s, Primitive types and Boxing Oh My!

I m pretty new to the Objective-C world and I have a long history with .net/C# so naturally I m inclined to use my C# wits. Now here s the question: I feel really inclined to create some type of ...

C# Marshal / Pinvoke CBitmap?

I cannot figure out how to marshal a C++ CBitmap to a C# Bitmap or Image class. My import looks like this: [DllImport(@"test.dll", CharSet = CharSet.Unicode)] public static extern IntPtr ...

How to Use Ghostscript DLL to convert PDF to PDF/A

How to user GhostScript DLL to convert PDF to PDF/A. I know I kind of have to call the exported function of gsdll32.dll whose name is gsapi_init_with_args, but how do i pass the right arguments? BTW, ...

Linqy no matchy

Maybe it s something I m doing wrong. I m just learning Linq because I m bored. And so far so good. I made a little program and it basically just outputs all matches (foreach) into a label control. ...

热门标签