Dependency inversion principle (DIP) states that high level modules should not depend on low level modules, both should depend on abstraction.

Abstraction should not depend on details, Details should depend upon abstraction.

Example

Without DIP

class FileLogger
{
    void Handle(string error)
    {
        File.WriteAllText(@"C:\Error.txt", error);
    }
}

internal class Customer
{
    FileLogger logger = new FileLogger(); // Bad

    public void Add(Database db)
    {
        try
        {
            db.Add();
        }
        catch (Exception error)
        {
            logger.Handle(error.ToString());
        }
    }
}

With DIP

class BetterCustomer
{
    ILogger logger;
    BetterCustomer(ILogger logger)
    {
        this.logger = logger;
    }

    void Add(Database db)
    {
        try
        {
            db.Add();
        }
        catch (Exception error)
        {
            logger.Handle(error.ToString());
        }
    }
}
class FileLogger : ILogger
{
    void Handle(string error)
    {
        File.WriteAllText(@"C:\Error.txt", error);
    }
}

interface ILogger
{
    void Handle(string error);
}

// e.g. when it is used:
void UseDependencyInjectionForLogger()
{
    var customer = new BetterCustomer(new FileLogger());
    customer.Add(new Database());
}