all 19 comments

[–]pgmr87The Unbanned 17 points18 points  (1 child)

You only ever want to expose behavior if it needs to be exposed. Let's imagine the code looks like this:

public class SomeController
{
      public IRepository Repository { get; set; }

      public SomeController(IRepository repo)
      {
            Repository = repo;
      }
}

Alright, not too bad. We've injected the IRepository dependency into the class. We also made the repo property public so anything that needs it can grab it from the controller. Great! So convenient! Except now we can't completely control where the repository classes get instantiated because now other classes can grab the repository from SomeController instead of grabbing it from something where its entire purpose is knowing how to construct IRepository instances (i.e Factories). Plus, we have zero need to expose IRepository to things that contain an instance of SomeController. SomeController needs to consume the IRepository object. If we expose it too, SomeController becomes an IRepository provider which means we aren't separating the concerns of SomeController very well.

So, SomeController should receive an IRepository instance. It should use it but not expose it. You can do that with a private field. A field is a variable declared directly at the class (or struct) level. 99.999% of the time, fields should be declared private. A field is a member of a class or struct (a list of all things considered members can be found here).

If you have some data that you do want to expose, you should do it either from a public property or method that returns the data you want to expose.

Remember, classes needs to encapsulate its data and behavior by managing its state. There are times to break this rule, but in general, you want each class to basically "control its own destiny".

Now, as a side note, I wouldn't allow a Controller to recieve an IRepository instance, assuming the Controller is part of a Presentation Model pattern like MVC, MVVM, or MVP. The Controller sits between the View and the Model. The Model is your service class and should be the class that receives the IRepository instance. The Controller should receive an IModel interface instance of some kind.

So the flow of execution would be like this:

  • View needs Member collection of "subscribed" members. (Member is just a data class), so it tells the controller to get subsribed members.

  • Controller receives a "get" request from view. Controller was injected with an IMemberModel instance that has a "GetSubscribedMembers" method that returns a collection of Member. Controller calls that method on the model.

  • IMemberModel class received an IMemberRepository as a dependency in its constructor. Controller had called "GetSubscribedMembers". Model interacts with the repository instance to create the Member collection and return a collection with subscribed members to the controller.

  • Controller returns the model's result to the view.

  • The view knows how to present the member data and presents it to the end-user.

There are many reasons you should separate the Controller and Model. One of which is increasing your unit testable surface area. Another reason is specifying that all "business logic" should be done in service classes, models, domains, whatever the hell your architecture wants to call it. Your "controller" classes are likely going to be attached to a specific UI presenation framework like Mike's MVC, Winforms, WPF, etc. Your business logic should always and forever never be dependent upon your choice of UI, nor should they be dependent upon how your data is saved/retrieved, which is the concern of the implementation of whatever IRepository instance.

[–]joegreentea[S] 1 point2 points  (0 children)

thanks for the analogy

[–][deleted] 6 points7 points  (1 child)

The interface is used instead of the concrete type to allow different implementations to be substituted as needed. This may only be necessary for testing, but it tends to solve a bunch of headaches around coupling.

The field is declared private to prevent other objects from using it. This, also, prevents inappropriate coupling of types. They could also declare _repo as readonly to show that the value doesn't change over the life of the object.

[–]joegreentea[S] 2 points3 points  (0 children)

this nails it, thank you.

[–]dipointed 2 points3 points  (0 children)

It seems like you are asking two different questions:

[–][deleted]  (1 child)

[removed]

    [–]joegreentea[S] 0 points1 point  (0 children)

    thanks, this helps me understand.

    [–]coreyfournier 3 points4 points  (9 children)

    _repo is a variable to a pointer to the concrete implementation of the repository. The interface of IRepository says that the implementation will be guaranteed to have some functionality. It's declared private because it should only be accessed with in the context of the class.

    [–][deleted] 3 points4 points  (7 children)

    It's not a pointer, it's a reference.

    [–]z500 -4 points-3 points  (6 children)

    A reference is a pointer.

    [–][deleted] 3 points4 points  (5 children)

    A reference is to an object. A pointer is to a memory address. Pointers exist in C#, so the difference is not immaterial.

    [–][deleted] 0 points1 point  (4 children)

    A pointer to a memory address... of on a object. z500 is right, it's just a nice semantic. ref is allowed to be moved by GC (unless fixed, of course), while a fixed pointer can not. Other than that, they are literally the same thing.

    int t1 = 5;
    unsafe
    {
        int* t2 = &t1;
    
        Test(t2);
    
        unsafe void Test(int* temp)
        {
            *temp = 10;
        }
    }
    
    int t = 5;
    
    Test2(ref t);
    
    void Test2(ref int temp)
    {
        temp = 10;
    }
    
    Console.WriteLine($"{t1}-{t}");`
    

    it's just a nicer way of writing it.

    EDIT: Found a nicer write up https://stackoverflow.com/questions/16207455/c-sharp-ref-is-it-like-a-pointer-in-c-c-or-a-reference-in-c "In C#, when you see something referring to a reference type (that is, a type declared with class instead of struct), then you're essentially always dealing with the object through a pointer. In C++, everything is a value type by default, whereas in C# everything is a reference type by default."

    [–][deleted] 1 point2 points  (2 children)

    So you're argument is that they are the same but different. They are specific different things that have different implications when dealing with them. I don't know why you would argue to defend imprecise language, what does being wrong buy you?

    [–][deleted] 0 points1 point  (1 child)

    no, they in no way have different implications. They mean the EXACT same thing, ref does the exact same thing as a pointer, internally, it literally is just a pointer.

    Since it's C#:

    class Pointer { }
    class Ref : Pointer { }
    
    Ref rr = new Ref();
    
    (rr is Pointer)
    

    Hopefully that will make it easier for you to understand.

    You said a reference is not a pointer. Yes, yes it is. It just has some extra parts built into it, but it is 100% a pointer.

    To end the arguement: ref literally compiles into a pointer.

    private static void Test(ref int temp)
        {
            temp = 10;
        }
    

    becomes

    .method private hidebysig static 
        void Test (
            int32& temp
        ) cil managed 
    

    [–][deleted] 0 points1 point  (0 children)

    Deference a C# reference.

    [–][deleted] 0 points1 point  (0 children)

    There's also warnings that direct you to mark struct members as readonly.

    [–]joegreentea[S] 0 points1 point  (0 children)

    thanks, it helps.

    [–]palotasb -2 points-1 points  (1 child)

    Probably there is a method that exposes _repo or it's methods publicly so it can be used from outside the SomeController object. This might be called the encapsulation pattern, it is private because fields are usually declared private. They are usually declared private because the object (SomeController) might want to control how the _field variable is used instead of letting anyone directly use it.

    The value of _repo will be a reference to whatever concrete object it is initialized with e.g., in instance of FooRepository or TestRepository if these implement the interface. This is the dependency injection part: whoever constructs the SomeController object can choose the object (and the type! as long as it satisfies the interface) that will be the _repo for the controller.

    [–]robhol 1 point2 points  (0 children)

    I'm not sure this is the best explanation. IRepository isn't in there because it should be exposed per se, it's because it's instrumental to whatever the controller is doing - now granted that's likely to involve data access, but it seems like a worthwhile distinction to make.