views:

78

answers:

3

I have a generic class DirectorySource<T> which depends on an interface IDirectorySearch<T>.

Both are generics and have the same type constraint:

public class DirectorySource<T> where T : IDirectoryEntry { }

public interface IDirectorySearcher<T> where T : IDirectoryEntry { }

So, for instance, when I want a source to manipulate groups, I would go this way:

IDirectorySearcher<Group> groupSearcher = new GroupSearcher(ROOT, SCOPE);
IDirectorySource<Group> groups = new DirectorySource(groupSearcher);

What I wish to come up with is to force, when my generic type DirectorySource<T> is of DirectorySource<Group> type, that my searcher is a GroupSearcher, and I don't want one to be able to pass in a UserSearcher, for example.

I have read the following articles:

  1. C#: Generic types that have a constructor?;
  2. An Introduction to C# Generics;
  3. new Constraint (C# Reference).

And I don't seem to get how to handle this kind of constraint with my class. As for now, I have the following:

public class DirectorySource<T> where T : IDirectoryEntry {
    public DirectorySource(IDirectorySearcher<T> searcher) {
        Searcher = searcher;
    }

    public IDirectorySearcher<T> Searcher { get; private set; }
}

And then, I have the following too:

public class GroupSearcher : IDirectorySearcher<Group> {

    public GroupSearcher(DirectoryEntry root, SearchScope scope) {
        NativeSearcher = new DirectorySearcher();
        NativeSearcher.SearchRoot = root;
        NativeSearcher.SearchScope = scope;
    }

    // Implementing interface...
}

I can't just replace the Searcher property type, as this would cause my generic class to become non-generic.

Any idea or something I didn't understand correctly about this constructor constraint on how I should go with what I want to accomplish?

EDIT #1

The reason I want to do so is because one could do the following:

IDirectorySearcher<User> userSearcher = new UserSearcher(ROOT, SCOPE);
IDirectorySource<Group> groups = new DirectorySource<Group>(userSearcher);

This seems incorrect to me...

1. Am I missing something obvious here? =)

Thanks in advance!

+3  A: 

What I wish to come up with is to force, when my generic type DirectorySource is of DirectorySource<Group> type, that my searcher is a GroupSearcher.

Why? That's going against the point of encapsulating the searcher functionality into an interface, surely? You shouldn't care what the implementation is, so long as it can search for the right kinds of entry.

I don't think constructor constraints are really relevant here - that would only allow you to create a new instance of T with no arguments...

EDIT: I can't see how your proposed problem is actually a problem. Let's look at the code:

IDirectorySearcher<User> userSearcher = new UserSearcher(ROOT, SCOPE);
IDirectorySource<Group> groups = new DirectorySource<Group>(userSearcher);

So here the T for DirectorySource<T> is Group. Now the constructor will require you to pass in an IDirectorySearcher<Group> - which userSearcher isn't, as far as I can see. So this code wouldn't compile, which is what you want. Where's the problem?

Jon Skeet
@Jon: Thanks for your answer. Why I want to make it so is because, as for now, one could do the following: (please see my edit #1).
Will Marcouiller
@Jon: If you see my **EDIT #1**, you might now better understand why I want to force it or constraint or whatsoever to a `GroupSearcher`, to continue with my example.
Will Marcouiller
@Jon: What do you think I should do instead? Is my design bad? =S
Will Marcouiller
@Will: It's not clear to me how that would compile. You'd be trying to create a `DirectorySource<Group>` passing in something that didn't implement `IDirectorySearcher<Group>` so it wouldn't compile as far as I can see. Have you tried it?
Jon Skeet
@Jon: I'm quite new to generics, and I thought that the `T` from `DirectorySource<T>` and the `T` from `IDirectorySearcher<T>` were completely independant, even though `T` was specified in the source. To answer your question, I had not tested it yet, because I have written this code in VB.NET 2.0 which disallows me to use plenty of features that I used to use in C#, so I'm rewriting it in C# the way I would do it, then use a converter from C# to VBNET afterwards, once all tested. Besides, with your EDIT, I see no problem at all! =) Thanks, this helps! =)
Will Marcouiller
+2  A: 

Have you considered:

public class DirectorySource<TValue, TSearcher> 
            where TValue : IDirectoryEntry 
            where TSearcher : IDirectorySearcher<T>, new() 
{ 
    public DirectorySource(TSearcher seacher) 
    { 
        Searcher = seacher
    } 

    public TSearcher Searcher { get; private set; } 
} 

IDirectorySearcher<Group> groupSearcher = new GroupSearcher(ROOT, SCOPE);     
var  groups = new DirectorySource<Group, GroupSearcher>(groupSearcher); 
James Curran
+1 Nope, I have not even though of it a bit.
Will Marcouiller
I don't think this is necessary... I can't see why it would be. See my edit.
Jon Skeet
I've done this as well in some situations where I thought it made sense. I was under the impression that this approach would compile such that any calls to methods on the TSearcher instance would be direct calls to the actual type's methods instead of virtual calls through the interface. I tried this out and found that my understanding was not actually the case (according to ILDASM). Not that performance is generally so critical for me that I care about whether calls are virtual or not, but at least that was one benefit that I could think of for this approach. Am I missing something?
Dr. Wily's Apprentice
-bonk self- Well, I think I was being silly; looking at the IL wouldn't do much good since the generic parameters are effectively evaluated by the JIT compiler at runtime (someone correct me if I'm off-base). Therefore, what really matters is how the JIT compiler handles it. I *think* I read something suggesting that the JIT compiler would not use interface method dispatch in this situation, but maybe I'm mistaken.
Dr. Wily's Apprentice
@Jon, I was assuming that he really, really wanted the Searcher property to return a `GroupSearcher` and not merely an `IDirectorySearcher<Group>`
James Curran
+1  A: 

I think the only way you're going to get what you want (as I understand it) is if you add a GetSource method to IDirectorySearcher which returns an implementation of IDirectorySource<T>.

Donnie
+1 Not a bad idea. Yet when I want to apply the pending changes, it is my source's duty, and not the searcher. In other words, I would like to make my `GroupSearcher` part of my `DirectorySource`, but this comes quite a bad design when I want to merge those two together.
Will Marcouiller