The Random Facts of Coding (or so it seems)

Fernando Vicaria

Encapsulation 101

After seeing this sort of issue come up in many projects in the shape of hard to spot bugs I decided to quickly write a small entry as a sort of OOP Commandment.

Thou shalt not return references to internal fields!

If you took the trouble of hiding an internal field making it private or read-only don't send it all to waste by handling it to the first caller of your class method.

A simple example of what I am talking about is as follows:

 

 

using System;

using System.Collections.Generic;

using System.Text;

 

public class MyObject

{

    string m_Name;

   

    public string Name

    {

        get { return m_Name; }

        set { m_Name = value; }

    }

}

 

public class MyObjectHolder

{

    private MyObject m;

 

    public MyObjectHolder()

    {

        m = new MyObject();

        m.Name = "MyObject";

    }

 

    public MyObject GetMyObject()

    {

        return m;

    }

 

    public string GetName()

    {

        return m.Name;

    }

}

 

class Program

{

    static void Main(string[] args)

    {

        MyObjectHolder h = new MyObjectHolder();

        Console.WriteLine(h.GetName());

       

        // The private member now is not private anymore...

        MyObject m = h.GetMyObject();

        // Any changes in m will affect the original internal field of h.

        m.Name = "SomeOtherName";

      

        Console.WriteLine(h.GetName());

    }

}

 

 

There are at least three options for this simple case. One is to turn the internal field into a ValueType, a struct in this case will fullfil the objective of holding the data.

The second would be create a temporary local variable inside GetName and assign it the same values for every field. The easiest way to get that is to make your object implement ICloneable (with a deep copy implementation).

The third option is to return an interface to the object with limited capabilities.

Here is a possible implementation:

 

 

public interface IMyObject

{

    string GetName();

}

 

public class MyObject: IMyObject

{

    string m_Name;

   

    public string Name

    {

        get { return m_Name; }

        set { m_Name = value; }

    }

 

    public string GetName()

    {

        return m_Name;

    }

}

 

public class MyObjectHolder

{

    private MyObject m;

 

    public MyObjectHolder()

    {

        m = new MyObject();

        m.Name = "MyObject";

    }

 

    public IMyObject GetMyObject()

    {

        return m;

    }

 

    public string GetName()

    {

        return m.Name;

    }

}

 

class Program

{

    static void Main(string[] args)

    {

        MyObjectHolder h = new MyObjectHolder();

        Console.WriteLine(h.GetName());

       

        // The private member now is not private anymore...

        IMyObject m = h.GetMyObject();

        // Any changes in m will affect the original internal field.

        //m.Name = "SomeOtherName"; <-- error : does not contain Name.

      

        Console.WriteLine(h.GetName());

    }

}

 

 

Although String is also a reference type it is also an immutable type so it’s ok to return a direct reference to the internal string field. Any attempt to modify it will create a new string.

A caveat of the interface solution is that the client of your class can still guess the real type of the interface reference returned and force a cast. But then again if a developer goes through such an effort he deserves what he gets.

That’s it. I hope that helps you to avoid a couple of bugs here and there.

Published Thursday, February 16, 2006 4:54 PM by fvicaria

Comments

No Comments
Anonymous comments are disabled

© 2009 Microsoft Corporation. All rights reserved. Terms of Use  |  Trademarks  |  Privacy Statement
Microsoft
Page view tracker