How to hide some parts of your API in C#

I have spent my last 3 years working on Plainconcepts in an amazing project called WaveEngine, this is a 2D/3D multiplatform game engine. And I learn a lot of things by working on this project, because I think it is very different develop apps using some libraries or frameworks than to develop a library or a framework for others developers who will later create apps using it. The first thing is that your customer are developer intead of final user and developer are very demanding with the libraries that they use (representative names of methods, intellisense documentation, clean API, consistency are some examples).

 

So when you work on a library, you sometimes need to hide some parts of your API like methods or properties which you don’t want the final developers to have access to or to change directly, or simply because you want to offer a clean API to these final developers. I would like to show you some techniques to get this behavior.

 

Using visibility operators like internal or protected internal

We need to have a Game class with a Draw method, which will be called from the main loop of our graphics framework. If our graphics framework needs to synchronize some methods or needs to prepare properties before calling the Draw method, to make it easier others task for our users we could make this method invisible in our API. Using internal this method will be accessible only from classes in the same assembly.

public class Game 

{

    ...

 

    internal void Draw()

    {

        // Render

    }

 

    ...

}

Draw method will be “public” for us from our assembly but invisible for our users.

 

Another possibility is if our users need to extend a framework class and override one of its methods, and we need to be able to call this new method from another class but we don’t want the final developer to call this method from another class.

public class Component

{

    internal protected abstract void ResolveDependencies();

}

 

public class UserComponent : Component

{

    protected internal override void ResolveDependencies()

    {

    }

}

 

We can call the ResolveDependencies method written for our user from other classes inside of our framework assembly, but for the final developer this method is only protected, so they can’t call this method from other classes in their code.

Our view:

public Manager()

{

    foreach (Component component in components)

    {

        component.ResolveDependencies();

    }

}

 

User view:

static void Main(string[] args)

{

    UserComponent component = new UserComponent();

 

    // Error: UserComponent.ResolveDependencies() cannot change access modifiers 

    // when overriding 'protected' inherited member Component.ResolveDependencies()

    component.ResolveDependencies();

}

Using explicit interfaces implementation

Explicit interfaces implementation is a great technique in C# that allows us to clean our final API. This is the most common technique used by Microsoft in all their APIs. The situation is, imagine that you need some extra method to simplify the final API for users, but all this aux method blurred the final API.

So if you make an explicit implementation for these aux methods, those methods will only be visible if you do a casting to the specific type.

public interface InternalMethods

{

    void Resume();

    void Active();

    void Execute();

}

 

public class GameObject : InternalMethods

{

    public void Play();

 

    public void InternalMethods.Resume()

    {

    }

 

    public void InternalMethods.Active()

    {

    }

 

    public void InternalMethods.Execute()

    {

    }

}

 

How to call each method:

class Program

{

    static void Main(string[] args)

    {

        GameObject obj = new GameObject();

        obj.Play();

 

        (obj as InternalMethods).Resume();

        (obj as InternalMethods).Active();

        (obj as InternalMethods).Execute();

    }

}

 

When our user uses intellisense on the GameObject instance, they will only see the Play method (clean API), and if he does a casting to the interface then he could access all the methods from the specific interface.

 

Using new to override methods

Imagine this situation, we want to draw 3D and 2D elements, to do that we need to call the low level API in DirectX so that all is drawn using 3D vectors (a 2D sprite is a 3D plane oriented to the camera any Z plane). But the user wants a 2D simplified API that only shows them 2D vectors.

We are going to create a base class Transform3D where we have all the properties needed to draw 3d models, plus we will have a Transform2D class with some helper methods and with some extra properties like zoom or flip options.

Internally you need all transform3D properties to be able to render the 2D elements, but your users want to see a simple API with only 2D vector properties. To solve this situation we will use the new keyword to override some methods and properties.

public class Transform3D

{

    protected Vector3 position;

 

    public Vector3 LocalPosition

    {

        get

        {

            return this.position;

        }

 

        set

        {

            this.position = value;

        }

    }

}

 

public class Transform2D : Transform3D

{

    public new Vector2 LocalPosition

    {

        get

        {

            return new Vector2(this.position.X, this.position.Y);

        }

 

        set

        {

            this.position.X = value.X;

            this.position.Y = value.Y;

        }

    }

}

 

Using this technique our user will only see the 2D LocalPosition property version from Transform2D and to access to the 3D LocalPosition property version they will need to do a casting to Transform3D like this:

Transform3D t1 = new Transform3D();

t1.LocalPosition = new Vector3(10);

 

Console.WriteLine(t1.LocalPosition.ToString());

 

Transform2D t2 = new Transform2D();

t2.LocalPosition = new Vector2(20);

Console.WriteLine(t2.LocalPosition.ToString());

 

(t2 as Transform3D).LocalPosition = new Vector3(30);

Console.WriteLine((t2 as Transform3D).LocalPosition.ToString());

image

 

It is important to remember that the purpose of all these techniques are only simplify the final API for the developers who use your libraries or frameworks.

 

I hope you enjoyed reading this post XD

Publicado por

jcanton

Javier is a Computer Science Engineer who has always had a passion for 3D graphics and software architecture. He learned C# almost at the same time as he learned to talk, and his first word was "base". He enjoys imparting talks about technology and has contributed in many important software and video game events. He has participated in multitude of software projects involving multitouch technologies, innovative user interfaces, augmented reality, and video games. Some of these projects were developed for companies such as Microsoft, Syderis, and nVidia. His professional achievements include being MVP for Windows DirectX and DirectX XNA for the last eight years, Xbox Ambassador, as well as Microsoft Student Partner and Microsoft Most Valuable Student during his years at college. Currently he works at Plainconcepts and he is the development team lead at WaveEngine project.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *