interface ICovariant<out T>
{
	T Foo { get; }
}

interface IContravariant<in T>
{
	int Bar (T bar);
}

interface IBothVariants <out T1, in T2> : ICovariant<T1>, IContravariant<T2>
{
}

interface IInvariant <T> : ICovariant<T>, IContravariant<T>
{
}

class BothVariants <T1, T2> : IBothVariants <T1, T2>
{
	public BothVariants (T1 foo)
	{
		Foo = foo;
	}

	public T1 Foo { get; private set; }

	public int Bar (T2 bar)
	{
		return bar.GetHashCode () ^ Foo.GetHashCode ();
	}
}

class Invariant <T> : IInvariant<T> where T : new()
{
	public T Foo { get { return new T (); } }

	public int Bar (T bar)
	{
		return bar.GetHashCode ();
	}
}

class A
{
	public virtual string Fruit { get { return "Apple"; } }
}

class B : A
{
	public override string Fruit { get { return "Banana"; } }
}

class C : B
{
	public override string Fruit { get { return "Carrot which I know is not a fruit but you better shut up about it before I cut you"; } }
}

public class Test
{
	public static int Main ()
	{
		var b = new B ();
		var c = new C ();

		IBothVariants<A, C> both = new BothVariants<B,B> (b);

		if (both.Bar (c) != (b.GetHashCode () ^ c.GetHashCode ()))
			return 1;

		IInvariant<B> neither = new Invariant<B> ();
		ICovariant<A> co = neither;
		if (co.Foo.Fruit != "Banana")
			return 2;

		IContravariant<C> contra = neither;
		if (contra.Bar (c) != c.GetHashCode ())
			return 3;

		return 0;
	}
}