Reputation: 3
I have been provided with two libraries, each with their own namespaces, which have identical class structures for the request part of the api, but different responses.
I want to write a function that can handle both requests irrelevant of their namespace type (I have experimented with generics but failed to create a solution).
Here is an example of what I am trying to do:
public void Function1()
{
Namespace1.MyType type1 = new Namespace1.MyType1();
type1.InnerType = new Namespace1.MyType2();
type1.PerformMethod1();
}
public void Function2()
{
Namespace2.MyType type1 = new Namespace2.MyType1();
type1.InnerType = new Namespace2.MyType2();
type1.PerformMethod1();
}
I have tried to use generics at the method level in the following way:
public void Function1<TType1, TType2>()
where TType1: NamesSpace1.Type1, NamesSpace2.Type1, new()
where TType2: Namespace1.Type2. Namespace2.Type2(), new()
{
TType1 type1 = new TType1();
type1.InnerType = new TType2();
type1.PerformMethod1();
}
However, .net does not allow this level of constraints on generics. Any suggestions on how I could create a generic function would be great.
Thanks
Upvotes: 0
Views: 947
Reputation: 174389
The short answer is: You can't.
C# (or .NET for that matter) doesn't support "Structural Subtyping", making these two classes completely separate, although they have the same members.
You could however make use of the dynamic
keyword, but you would lose all compile time type safety.
publiv void Function(dynamic type1)
{
type1.PerformMethod1();
}
You still would have to create the two different instances of type1
and type2
, so you don't really gain much here.
Another possible solution with basically the same drawbacks is to use reflection to create the types:
public void Function(string assemblyName, string namespace)
{
var type1Name = namespace + ".Type1, " + assemblyName;
var type2Name = namespace + ".Type2, " + assemblyName;
dynamic type1 = Activator.CreateInstance(Type.GetType(type1Name));
dynamic type2 = Activator.CreateInstance(Type.GetType(type1Name));
type1.InnerType = type2;
type1.PerformMethod1();
}
And a third solution would be to use generics only with the new()
constraint to be able more easily create the instances and use runtime type checking to ensure that only the allowed types have been used. Again, this has the same drawbacks as the two other solutions: No compile time type safety:
public void Function1<TType1, TType2>()
where TType1 : new()
where TType2 : new()
{
if(typeof(TType1) != typeof(Namespace1.MyType1) ||
typeof(TType1) != typeof(Namespace2.MyType1) ||
typeof(TType2) != typeof(Namespace1.MyType2) ||
typeof(TType2) != typeof(Namespace2.MyType2))
throw new ArgumentException(...);
// .NET 4:
dynamic type1 = new TType1();
dynamic type2 = new TType2();
// Pre .NET 4:
// var type1 = new TType1();
// var type2 = new TType2();
// .NET 4:
type1.InnerType = type2;
// Pre .NET 4:
// type1.GetType().GetProperty("InnerType").SetValue(type1, type2);
// .NET 4:
type1.PerformMethod1();
// Pre .NET 4:
// type1.GetType().GetMethod("PerformMethod1").Invoke(type1, new object[0]);
}
Upvotes: 2
Reputation: 244938
Generics in .Net really don't work that way.
I think the best way to do this is to create a common interface and somehow fit in into the library classes. The question is how to do that.
If the type in question is not sealed and you are creating its instances yourself, then you can create a derived class that implements this interface, which will be automatically implemented by the inherited methods:
interface IMyType
{
object InnerType { get; set; }
void PerformMethod1();
}
class Namespace1MyTypeWithInterface : Namespace1.MyType, IMyType
{}
This assumes both types are actually identical. If, for example, the type of InnerType
is different in each, the situation gets more complicated. One way to solve that would be to make the interface generic in the type of the property.
If the previous solution won't work for you, then you can create adapter class for each namespace. Both adapters would again share the same interface and they would delegate each call to an instance of the actual type.
class Namespace1MyTypeAdapter : IMyType
{
private readonly Namespace1.MyType m_adapted;
public Namespace1MyTypeAdapter(Namespace1.MyType adapted)
{
m_adapted = adapted;
}
public object InnerType
{
get { return m_adapted.InnerType; }
set { m_adapted.InnerType = value; }
}
public void PerformMethod1()
{
m_adapted.PerformMethod1();
}
}
It's a lot of repeated code, but you write it once and then you can use both classes the same way.
Yet another option is to use dynamic
or reflection.
Upvotes: 1