I was looking at some Silverlight sample code and came across following
class declaration
public class RepositoryBase<C> : IDisposable
where C : ObjectContext, new()
I understand some part but what is the 'where .....' clause
Can someone give me some intro. I don't even know what to google for
Let me try and explain in some details. I will try and evolve the
same example to show all the points.
(the example is not very interesting, but it illustrates the points)
We start with:
using System;
namespace E
{
public class A
{
public override string ToString()
{
return "I am an A";
}
}
public class B
{
public override string ToString()
{
return "I am a B";
}
}
public class WrapperA
{
private A _data;
public A Data
{
get { return _data; }
set { _data = value; }
}
}
public class WrapperB
{
private B _data;
public B Data
{
get { return _data; }
set { _data = value; }
}
}
public class Program
{
public static void Main(string[] args)
{
WrapperA a = new WrapperA();
a.Data = new A();
Console.WriteLine(a.Data);
WrapperB b = new WrapperB();
b.Data = new B();
Console.WriteLine(b.Data);
Console.ReadKey();
}
}
}
Problem: WrapperA and Wrapper B actually contains the same code
and we are not a happy with that. We could solve the problem
by making data object, but that would not be type safe.
Solution: generics.
using System;
namespace E
{
public class A
{
public override string ToString()
{
return "I am an A";
}
}
public class B
{
public override string ToString()
{
return "I am a B";
}
}
public class Wrapper<T>
{
private T _data;
public T Data
{
get { return _data; }
set { _data = value; }
}
}
public class Program
{
public static void Main(string[] args)
{
Wrapper<A> ga = new Wrapper<A>();
ga.Data = new A();
Console.WriteLine(ga.Data);
Wrapper<B> gb = new Wrapper<B>();
gb.Data = new B();
Console.WriteLine(gb.Data);
Console.ReadKey();
}
}
}
Wrapper<T> can now be used for both A and B.
But now we want to do more in the wrapper than just get
and set. Let us try and add a SayHi method.
using System;
namespace E
{
public class A
{
public override string ToString()
{
return "I am an A";
}
public void SayHi()
{
Console.WriteLine("Hi");
}
}
public class B
{
public override string ToString()
{
return "I am a B";
}
public void SayHi()
{
Console.WriteLine("Hi");
}
}
public class Wrapper<T>
{
private T _data;
public T Data
{
get { return _data; }
set { _data = value; }
}
public void SayHi()
{
Data.SayHi(); // <---- compile error
}
}
public class Program
{
public static void Main(string[] args)
{
Wrapper<A> ga = new Wrapper<A>();
ga.Data = new A();
Console.WriteLine(ga.Data);
ga.SayHi();
Wrapper<B> gb = new Wrapper<B>();
gb.Data = new B();
Console.WriteLine(gb.Data);
gb.SayHi();
Console.ReadKey();
}
}
}
Problem: this does not compile as the compiler have no way
to know whether T will have a SayHi method or not.
Solution: put a constraint on T that ensure that T must
have a SayHi method.
using System;
namespace E
{
public interface IHiSayer
{
void SayHi();
}
public class A : IHiSayer
{
public override string ToString()
{
return "I am an A";
}
public void SayHi()
{
Console.WriteLine("Hi");
}
}
public class B : IHiSayer
{
public override string ToString()
{
return "I am a B";
}
public void SayHi()
{
Console.WriteLine("Hi");
}
}
public class Wrapper<T> where T : IHiSayer
{
private T _data;
public T Data
{
get { return _data; }
set { _data = value; }
}
public void SayHi()
{
Data.SayHi();
}
}
public class Program
{
public static void Main(string[] args)
{
Wrapper<A> ga = new Wrapper<A>();
ga.Data = new A();
Console.WriteLine(ga.Data);
ga.SayHi();
Wrapper<B> gb = new Wrapper<B>();
gb.Data = new B();
Console.WriteLine(gb.Data);
gb.SayHi();
Console.ReadKey();
}
}
}
It works with IHiSayer interface and the constraint:
where T : IHiSayer
Note that the constraint can be either implementing an
interface or extending a class. I just picked the first
for the example.
But now we want to add another method that actually
returns a T instance.
using System;
namespace E
{
public interface IHiSayer
{
void SayHi();
}
public class A : IHiSayer
{
public override string ToString()
{
return "I am an A";
}
public void SayHi()
{
Console.WriteLine("Hi");
}
}
public class B : IHiSayer
{
public override string ToString()
{
return "I am a B";
}
public void SayHi()
{
Console.WriteLine("Hi");
}
}
public class Wrapper<T> where T : IHiSayer
{
private T _data;
public T Data
{
get { return _data; }
set { _data = value; }
}
public void SayHi()
{
Data.SayHi();
}
public T GiveMeAnother()
{
return new T(); // <---- compile error
}
}
public class Program
{
public static void Main(string[] args)
{
Wrapper<A> ga = new Wrapper<A>();
ga.Data = new A();
Console.WriteLine(ga.Data);
ga.SayHi();
Console.WriteLine(ga.GiveMeAnother());
Wrapper<B> gb = new Wrapper<B>();
gb.Data = new B();
Console.WriteLine(gb.Data);
gb.SayHi();
Console.WriteLine(gb.GiveMeAnother());
Console.ReadKey();
}
}
}
Problem: this does not compile as the compiler have no way
to know whether T will have a constructor with no arguments.
And it is not possible to specify a no arg ctor via an
interface or a super class.
Solution: a special constraint new().
using System;
namespace E
{
public interface IHiSayer
{
void SayHi();
}
public class A : IHiSayer
{
public override string ToString()
{
return "I am an A";
}
public void SayHi()
{
Console.WriteLine("Hi");
}
}
public class B : IHiSayer
{
public override string ToString()
{
return "I am a B";
}
public void SayHi()
{
Console.WriteLine("Hi");
}
}
public class Wrapper<T> where T : IHiSayer, new()
{
private T _data;
public T Data
{
get { return _data; }
set { _data = value; }
}
public void SayHi()
{
Data.SayHi();
}
public T GiveMeAnother()
{
return new T();
}
}
public class Program
{
public static void Main(string[] args)
{
Wrapper<A> ga = new Wrapper<A>();
ga.Data = new A();
Console.WriteLine(ga.Data);
ga.SayHi();
Console.WriteLine(ga.GiveMeAnother());
Wrapper<B> gb = new Wrapper<B>();
gb.Data = new B();
Console.WriteLine(gb.Data);
gb.SayHi();
Console.WriteLine(gb.GiveMeAnother());
Console.ReadKey();
}
}
}
Voila.
And now this example looks more like your example.
Arne