Hi everyone,

we were taught about Generics and Interfaces at uni and I wasn't sure what the perfect situations are to use interfaces because they don't have implementation in them so they work as a contract to other classes where everyone can implement their methods as long as they extend the interfaces.

As for generics, as far as I understand them, you make your own data structure or data type, right?

that would be greatly appreciated if someone can clarify those points.

Generics are used when you can apply the same situation to multiple objects. They are very much like templates in C++.

Let's take the "List" class.

The definition is List<T> where T is a type of object. As we don't really care what the list contains, the list can contain anything :)

This is why you specify the type when you create the List. Example: List<Int32> will contain a list of Int32s. This means when you want to perform an operation on an object in the list, it is strongly typed and you have access to all the functionality that object allows you. It also means you can't do things like adding a Double to a list of ints.

Generics can be as complicated or simple as you want them to be, but they can be extremely useful and powerful, especially when combined with reflection.

Another place generics can be used is on Method definitions.

For example:

Factory.CreateDataAdapter<T>() : where T : class, new()
{ 
    /* Do some reflection etc */
    return (T)reflectedInstance;
}

That's an extremely pointless method as far as functionality goes but serves as an example of how it can be used.

A common one used to be when creating DTOs with a primary key where the primary key was not always the same but you used the same base object and comparison objects

public class BaseDTO<T> where T : class
{
    public T Id
    { get; set; }

    public Boolean IdIsEqualTo(T idToCompare)
    {
        if(idToCompare == null)
            return false;

        return Id.Equals(idToCompare);
    }
}

public class MessageTable : BaseDTO<Guid>
{
    /* 
     * Code specific to MessageTable goes here
     * Also have access to BaseDTO, with strongly typed Id as Guid
     */
}

Also you don't have to use the build in types, there's nothing stopping you from using your own custom data types, much in the same way you probably use List<T> with your own types :)

Interfaces are typically used when multiple classes can perform the same function but have different implementations of the method.

A good example of this is typically done at the data access layer, where you may wish to offer different providers to your business logic.

public interface IDataAccess
{
    void SaveMyObject(MyObject objectToSave);
    MyObject FetchMyObject(Guid objectId);
}

public class MemoryStorageAccess : IDataAccess
{
    private List<MyObject> _objectList = new List<MyObject>();

    public void SaveMyObject(MyObject objectToSave)
    {
        MyObject obj = _objectList.Find(o => o.Id.Equals(objectToSave.Id));
        if(obj == null)
            _objectList.Add(objectToSave);
        else
            obj = objectToSave;
    }

    public MyObject FetchMyObject(Guid objectId)
    {
        return _objectList.Find(o => o.Id.Equals(objectId);
    }
}

public class DatabaseStorageAccess : IDataAccess
{
    private IDbConnection conn = new SqlConnection(Globals.CONNECTION_STRING);

    public void SaveMyObject(MyObject objectToSave)
    {
        // Call database procedure to save the object
    }

    public MyObject FetchMyObject(Guid objectId)
    {
        // Call database procedure to retrieve the object
    }
}

public class BusinessLogic
{
    IDataAccess _dataAccessObject;

    public BusinessLogic(IDataAccess dataObject)
    {
        _dataAccessObject = dataObject;
    }

    public void UpdateQuantity(Guid objectId, Int32 quantity)
    {
        MyObject currentObject = _dataAccessObject.FetchMyObject(objectId);
        currenObject.Quantity = quantity;
        _dataAccessObject.SaveMyObject(currentObject);
    }
}

You could also combine the above with Generics, to make the interface more powerful.

ie

public interface IDataAccess
{
    void SaveObject<T>(T objectToSave);
    TObject FetchObject<TObject, TId>(Tid idToSearch);
}

Hope this helps :)

This article has been dead for over six months. Start a new discussion instead.