tags:

views:

129

answers:

7

I'm writing a method in C# (2.0) that has to return a collection of simple objects. Normally I would do something like this:

class MyWidget
{
    struct LittleThing
    {
        int foo;
        DateTime bar;
    }

    public IList<LittleThing> LookupThings()
    {
        // etc.
    }
}

However, I have to declare this method in an interface. The caller doesn't get to see MyWidget, only an IWidget interface. The above setup doesn't work in that situation, because C# does not allow defining types inside an interface. What is the proper or best way to do such a declaration?

The straighforward thing I thought of is to simply declare LittleThing outside of the interface. That doesn't seem great, for a couple of reasons. One: it is only ever used by that single method in that single class, so it doesn't seem that LittleThing should be an independent type just floating around by itself. Two: if similar methods wind up being written for other classes, they will be returning different kinds of data (for good design reasons), and I don't want to clutter the namepace with a ton of similar-named structs that differ only slightly from each other.

If we could upgrade our version of .Net, I would just return a Tuple<>, but that's not going to be an option for some time yet.

[Edited to add: The small object does need to contain more than two fields, so KeyValuePair<K,V> won't quite cut it.]

[Edited to add further: IWidget is implemented by only one class, Widget. I think it weird to have an interface for only one class, but this was done to satisfy an old coding policy that required the contract to always be in a separate assembly from the implementation. Said policy has now gone away, but we haven't the resources to refactor the entire application and remove all the unnecessary interfaces.]

What's the best practice?

+1  A: 
  1. Why use structs? Why not use classes instead?
  2. Declare the class separately, as a public class.
Larry Watanabe
For my purposes, the only notable difference is that structs are slightly smaller. Since the object's contained data is small, and my list is potentially extremely large, the change has a noticeable effect on memory usage. If I need to make it a class later I need only change the keyword.
Auraseer
You're correct about the memory usage, but remember that structs are value types, not reference types. var s2 = otherstruct always makes a *copy* of all the fields in the struct, not a reference to the same struct!
Orion Edwards
Quite true. That's why I said "for my purposes." In the expected use, the caller doesn't go copying these objects around; he'll process or display the whole list. If it were sensible to, say, filter out large subsets and create new lists of them, then I'd want to use a class instead.
Auraseer
Copying a reference to an IList of value types does make copies of each value though.
recursive
recursive: That is not correct (unless I'm misunderstanding your statement). Copying a reference only copies the reference. It does not automatically clone the whole collection. For instance, if I invoke method `DoStuff( IList<int>)` with a list of 10,000 entries, that does not allocate and copy 10,000 new integers.
Auraseer
+5  A: 

If the "LittleThing" only has two values, you can return a KeyValuePair<TKey,TValue>.

If there are more than two, you could always make your own Tuple class, and replace it with .NET 4's when you finally do move to .NET 4.

Otherwise, I would just define the struct with the interface, and include it as part of your API. Namespaces take care of the naming concern...

Reed Copsey
+1  A: 

You can declare the struct outside the interface, but inside a nested namespace.

ChrisW
This wont work if the namespace is the same as the interface name, which is more or less what he is trying to achieve.
Arc
Perhaps put the interface and the struct, both, in the nested namespace: and then any class which wants to use the two of them (use the interface and its associated struct) can do a `using` on that namespace.
ChrisW
A: 

Ignoring the meaning of the struct name here, you could use generic version of KeyValuePair.

shahkalpesh
+1  A: 

If we could upgrade our version of .Net, I would just return a Tuple<>, but that's not going to be an option for some time yet.

Why wait? It's not like a tuple is a complicated thing. Here's the code for a 3-tuple.

public struct Tuple<TItem1, TItem2, TItem3>
{
    public Tuple(TItem1 item1, TItem2 item2, TItem3 item3)
    {
        this = new Tuple<TItem1, TItem2, TItem3>();
        Item1 = item1;
        Item2 = item2;
        Item3 = item3;
    }

    public static bool operator !=(Tuple<TItem1, TItem2, TItem3> left, Tuple<TItem1, TItem2, TItem3> right)
    { return left.Equals(right); }

    public static bool operator ==(Tuple<TItem1, TItem2, TItem3> left, Tuple<TItem1, TItem2, TItem3> right)
    { return !left.Equals(right); }

    public TItem1 Item1 { get; private set; }
    public TItem2 Item2 { get; private set; }
    public TItem3 Item3 { get; private set; }

    public override bool Equals(object obj)
    {
        if (obj is Tuple<TItem1, TItem2, TItem3>)
        {
            var other = (Tuple<TItem1, TItem2, TItem3>)obj;
            return Object.Equals(Item1, other.Item1)
                && Object.Equals(Item2, other.Item2)
                && Object.Equals(Item3, other.Item3);
        }
        return false;
    }

    public override int GetHashCode()
    {
        return ((this.Item1 != null) ? this.Item1.GetHashCode() : 0)
             ^ ((this.Item2 != null) ? this.Item2.GetHashCode() : 0)
             ^ ((this.Item3 != null) ? this.Item3.GetHashCode() : 0);
    }
}

As you can see, it's no big deal. What I've done on my current project is implement 2, 3 and 4-tuples, along with a static Tuple class with Create methods on it, which exactly mirror the .NET 4 tuple types. If you're really paranoid you can use reflector to look at the dissassembled source code for the .NET 4 tuple, and copy it verbatim

When we eventually upgrade to .NET 4, we'll just delete the classes, or #ifdef them out

Orion Edwards
It Has Been Decided that I not create new generic containers for passing information around. If I really need to return more than one value, I'm to create a type with a meaningful typename and meaningful field names. (Don't ask me why Tuple will suddenly be okay as soon as it's a .net defined type. That's an argument I did not win.)
Auraseer
lol. It sounds like you're working in a fairly backward place... Are you even allowed to use Func<x,y,z> or any of that kind of thing?
Orion Edwards
Generics are fine for classes and methods-- for instance, I might have a CustomWeirdSortedCollection<IWidget>. The coding policy just says I can't create data-only generic containers. My guess is the policy writer was traumatized by some previous bad experience, with code that overused nameless containers and anonymous types where it should have had classes. I want to say "backward" is too harsh a description for this place, so I'd best not admit that they only upgraded to .Net 2 this past October.
Auraseer
A: 

Interfaces define only what can be implemented on another class, which is why you cannot give anything inside of them a definition. Including classes and structs.

That said, one pattern often used to get around this restriction is to define a class with the same name (excluding the 'I' prefix) to provide any related definitions, such as:

public interface IWidget
{
    IList<Widget.LittleThing> LookupThings();
}

// For definitions used by IWidget
public class Widget
{
    public struct LittleThing
    {
        int foo;
        DateTime bar;
    }
}

Examples of this pattern can be found in the BCL, particularly with generics and extension methods but also with default values (e.g. EqualityComparer<T>.Default) and even default implementations (e.g. IList<T> and List<T>). The above is just another case.

Arc
I'm not sure I understand what you mean here. Isn't this just a circular reference? My Widget class implements IWidget, so the interface can't depend back on its implementor. Also, since my caller only sees the interface, he wouldn't be able to resolve Widget.anything.
Auraseer
I mean that the Widget class (which I should have made a static class) should be defined in your library with IWidget. Its the naming that couples the interface with the class. This class does not implement the interface, but rather provide any related definitions since interfaces cannot define anything. Similar to what the C# EqualityComparer<T> class does for IEqualityComparer<T>. I'm assuming your implementing class is being called "MyWidget", since calling it "Widget" would be confusing.
Arc
Ah, I get it now. Your Widget class here contains no functionality, and only exists for other types to be nested inside? That looks equivalent to making a namespace. IMO an actual namespace would be preferable, because it's more commonly seen, and is usable with the using directive.
Auraseer
It is essentially used like a namespace, but can define other things as well as classes (extension methods of commonly used functionality, default values, etc). This is done to get around the fact that interfaces cannot nest classes/interfaces. And since namespaces are containers of common things. The dependence you have is stronger than what namespaces are intended to group. If you plan to group under a namespace, it may be better just to call it "WidgetLittleThing" or "IWidget_LittleThing" instead to emphasize that dependence.
Arc
A: 

So, reading all you have commented here is what I think:

  1. If all types implementing IWidget return LittleThing:

    Then I consider best practice to LittleThing be at the same namespace level than IWidget, preferably on a Widgets namespace. However you really should consider making LittleThing a class. By your description of the problem it seems it can be not so little, as everytime class implementing IWidget misght use some fields but not others.

  2. If all types implementing IWidget need to return slightly different values, but with similar behavior:

    Consider making an IThing interface. Then every IThing implementation would be entirely dependant on the class that returns it, so then you can declare the structure or class implementing IThing inside the class implementing IWidget like this:

    interface IWidget
    {
        IList<IThing> LookupThings()
        {
            …
        }
    }
    
    
    interface IThing
    {
        …
    }
    
    
    class MyWidget : IWidget
    {
        IList<IThing> IWidget.LookupThings()
        {
             …
        }
    
    
    
    private class MyWidgetThings : IThing
    {
         …
    }
    
    }
Wilhelm