views:

56

answers:

3

Ok, I'm on the verge of overthinking this. Is there a way to combine interfaces and attributes such that an attributed property in the implementing class fulfills the interface contract?

In my app I'd like to show a list of activities, which is an aggregation of events in the system such as a new message, a new task being created/assigned/updated, etc. It's just a quick hand list of what's been going on.

I thought about using an IIsActivity interface to flag entities that need to be a part of this aggregation. I really only need to show an activity title, the date it occurred, and link to the relevant area in the application. I don't want to enforce additional properties in the inheriting classes that simply end up duplicating information though. For example, a Task has an AssignedOn (DateTime), and a Message has a CreatedOn (DateTime). Both of this would fulfill the date requirement to be considered an Activity. What I don't want is a separate property just for the activity date. Same thing can go for the title (Message has a Title property, Task has a Name property).

So, I basically want to be able to say, "Get me anything this is an IIsActivity. Within each of those implementors, I expect there to be a property marked with an ActivityTitle attribute, and one with an ActivityDate attribute."

Too much?

+1  A: 

I think your best approach is to use explicit interface implementation.

public interface IActivity
{
  DateTime OccurredOn { get; }
}

public class Task : IActivity
{
  public DateTime AssignedOn
  {
    get { /* implemenation */ }
  }

  DateTime IActivity.OccurredOn
  {
    get { return AssignedOn; }
  }
}

public class Message : IActivity
{
  public DateTime CreatedOn
  {
    get { /* implemenation */ }
  }

  DateTime IActivity.OccurredOn
  {
    get { return CreatedOn; }
  }
}

And then you could use your classes like so:

public static void Main()
{
  Task task = new Task();
  Console.WriteLine(task.AssignedOn); // OK
  Console.WriteLine(task.OccurredOn); // Compile Error
  IActivity activity = task as IActivity;
  if (activity != null)
  {
    Console.WriteLine(activity.AssignedOn); // Compile Error
    Console.WriteLine(activity.OccurredOn); // OK
  }
}
Brian Gideon
A: 

This is not exactly your design but I have used attributes to enforce some of the properties of my classes have a desired value. I'm consuming a webservice and I have to send some information , some of the fields require value, others dont...

[Serializable]
    [AttributeUsage(AttributeTargets.Property)]
    public class RequiredAttribute : Attribute
    {
        private CheckType[] _requiredtype;
        public RequiredAttribute(params CheckType[] type)
        {
            _requiredtype = type;
        }
        public CheckType[] Requires
        {
            get { return _requiredtype; }
        }
        public static bool CheckRequirements(object applyto, out string errormessages)
        { ...   }

        private static bool RequiredSucceeded(object applyto, StringBuilder resultmessage)
        { ...  }
    }
    [Serializable]
    public enum CheckType
    {
        HasValue,       // Checks that the property value is not null
        CheckMinRequirements,  // Will enforce the validation of properties on defined types
        IsNotNullorEmpty, // For strings
        HasElements,  // Elements exist on arrays
        ElementsRequirements // for collections
    }

Now there is an example of a class using the attribute

[Serializable]
    public class PurchaseInsurance
    {
        [Required(CheckType.IsNotNullorEmpty)]
        public string PartnerBookingID
        {
            get;
            set;
        }
        [Required(CheckType.IsNotNullorEmpty)]
        public string Currency
        {
            get;
            set;
        }
        public string ReferringURL;

        [Required(CheckType.HasValue, CheckType.CheckMinRequirements)]
        public PurchaserInfo Purchaser
        {
            get;
            set;
        }
        [Required(CheckType.HasValue, CheckType.ElementsRequirements)]
        public InsuranceProduct[] Products
        {
            get;
            set;
        }
...
}

Before sending the data to the webService I will check if each property complies with their attributes tags.

jmayor
A: 

Althouth the CLR supports it, C# doesn't support interface implementation aliasing. You could achieve it in VB.NET though:

Public Interface IActivity ' this can even be in C# ' 
  ReadOnly Property OccurredOn() As DateTime
End Interface

Public Class Task
  Implements IActivity

  Public ReadOnly Property AssignedOn() As DateTime Implements IActivity.OccurredOn
    Get
      ' implementation... '
    End Get
  End Property

End Class

Public Class Message
  Implements IActivity

  Public ReadOnly Property CreatedOn() As DateTime Implements IActivity.OccurredOn
    Get
      ' implementation... '
    End Get
  End Property

End Class

So, as mentioned by Brian Gideon, explicit interface implementation is what comes closer to your goal in C#.

Jordão