4

I had an interesting discussion with a coworker that revolved around how people interpret the use of properties and methods on an interface. For example, let's say we have a blog with posts in various statuses: Working and Published.

When in "Working" the author is still making changes and shouldn't be visible to readers. When "Published", well... it's published and end users can read it.

Let's also say we have an interface for the Blog, which I defined as:

public interface IBlog
{
    IEnumerable<Post> Posts { get; }
    IEnumerable<Post> PublishedPosts { get; }
}

My coworker was worried that users of this interface might interpret these two properties as separate collections of objects. I figured since you have a "Posts" property, and another property called "Published Posts" that returns the same kind of object that people would make the correct assumption that Posts is one collection, and PublishedPosts is a filtered view of the Posts collection.

His suggestion was:

public interface IBlog
{
    IEnumerable<Post> Posts { get; }
    IEnumerable<Post> GetPublishedPosts();
}

Basically, replace the PublishedPosts property with a GetPublishPosts() method. He said this was more idiomatic for C#, because a method communicates to people using the interface that you are performing an operation on the "Posts" collection (filtering it by status). I haven't really seen any formal documentation for this, but that doesn't stop the C# community from leaning one direction or the other.

Is having one collection property, and then methods to filter the collection, or having two collections where the second filters the first idiomatic for C#? If so, is there formal documentation anywhere?

  • 2
    If so, is there formal documentation anywhere? There's no formal documentation for what's idiomatic. There can't really be. What's idiomatic is just what's commonly done. Nobody can formally document that. – Servy Jan 12 '17 at 21:39
  • I don't think either belong in an interface. If only there were extension properties – Caleth Jan 12 '17 at 23:13
  • Why not using an extension method? There is no need to implement the same function each time you implement the interface. – JanDotNet Jan 13 '17 at 8:10
  • @JanDotNet: That's an interesting suggestion. I hadn't thought of that. – Greg Burghardt Jan 13 '17 at 12:53
6
IEnumerable<Post> PublishedPosts { get { return Posts.Where(p => p.Published) }; }

if you want a single place for your filtering logic. Or, simply filter as needed:

var publishedPosts = myBlog.Posts.Where(p => p.Published);

The second version doesn't require you to go to the property definition to determine what it does. Don't make the users of your class guess; if the naming (which is what this is really all about) doesn't make things clear, then use better property names, provide adequate comments or let them see the source code.

I don't recommend the use of a method here. Methods imply processing and a return delay (i.e. more than 50ms), and there isn't any processing taking place here beyond the use of the filtering function. A method also implies that you're making copies of the original blog posts rather than returning a collection of references, which is probably not what you want.

I personally prefer the ad-hoc filter, because it allows you to work with a single, unambiguous property containing your blog post collection. A common place for your filtering logic is not really needed unless you plan on overhauling your architecture in the future, and if you really need that, hand your filter a higher-order function:

Func<Post, bool> published = p => p.Published; // one place to change filter logic
var publishedPosts = myBlog.Posts.Where(published);

Guidance from Microsoft:

Do use a method, rather than a property, in the following situations:

  • The operation is orders of magnitude slower than a field set would be.
  • The operation is a conversion, such as the Object.ToString method.
  • The operation returns a different result each time it is called, even if the parameters do not change. For example, the Guid.NewGuid method returns a different value each time it is called. 8 The operation has a significant and observable side effect.
  • The operation returns a copy of an internal state.
  • The operation returns an array.

Use a method where the operation returns an array because to preserve the internal array, you would have to return a deep copy of the array, not a reference to the array used by the property. This fact, combined with the fact that developers use properties as though they were fields, can lead to very inefficient code.

  • 1
    This is how I implemented it, actually. But I think my coworker's concern was more about what a property communicates versus what a method communicates. – Greg Burghardt Jan 12 '17 at 21:27
  • Then use the second variant I offered. – Robert Harvey Jan 12 '17 at 21:27
  • 2
    Using the second variant means the logic for detecting a "published" blog post must be duplicated every place you need to deal with only the published posts. If the definition of "published" changes, now you need to refactor numerous parts of your application. – Greg Burghardt Jan 12 '17 at 21:29
  • 1
    Pay no attention to the downvoter. They've never written C# code. – Robert Harvey Jan 12 '17 at 21:30
  • 2
    @RobertHarvey So you just don't care that this doesn't even attempt to answer the question? The question didn't ask how to write a property that does this, it asks if it's idiomatic to do so. You don't even touch on the subject in the answer, even after the OP pointed this out in comments. Why someone would upvote a post that doesn't in any way actually answer the question is what's more confusing. – Servy Jan 12 '17 at 21:36
1

If we look at the .NET CLR we can see some examples of sets and subsets exposed by the same object. For example, consider the following commonly-used collections from System.Web:

HttpRequest.Item - Returns all tag/value pairs in the request

HttpRequest.Form.Item - Returns only those tag/value pairs found in the body

HttpRequest.QueryString.Item - Returns only those tag/value pairs found in the querystring

The second and third collections are subsets of the first collection above, so they have the same set/subset relationship as the blog posts in the question posed by the OP.

Based on this example I might suggest the following:

interface IBlog : IEnumerable<Post>
{
    IEnumerable<Post> PublishedPosts { get;}
}
0

To me IEnumerable<Post> GetPublishedPosts(); no more infers a filter than IEnumerable<Post> PublishedPosts { get; }

public interface IBlog
{
    IEnumerable<Post> Posts { get; }
    IEnumerable<Post> GetPublishedPosts();
} 

By definition an interface does not define or restrict an implementation
There is nothing stopping the following implementation

public class Post
{ }
public interface IBlog
{
    IEnumerable<Post> Posts { get; }
    IEnumerable<Post> GetPublishedPosts();
}
public class Blog : IBlog
{
    private List<Post> posts = new List<Post>();
    private List<Post> postsPublished = new List<Post>();
    IEnumerable<Post> IBlog.Posts { get { return posts; } }
    IEnumerable<Post> IBlog.GetPublishedPosts() { return postsPublished; }
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.