PDA

View Full Version : Open generic types


dougknows
05-31-2007, 08:22 PM
Hello,

Is there a way to define open generic types in Spring.NET? I have an object:

public interface IMyObject<T>
{
bool DoSomething();
}

public class MyObject<T> : IMyObject
{
public bool DoSomething()
{
// do something and return a bool
}
}

I'd like to be able to get the instance of MyObject<T> in the following consumer code:

IApplicationContext context = ContextRegistry.GetContext();
Type t = typeof(IMyObject<int>);
string objectName = context.GetObjectNamesForType(t)[0];
IMyObject<int> mo = context.GetObject(objectName) as IMyObject<int>;
bool value = mo.DoSomething();

Is there any way I can define this open generic type? I want any request for IMyObject<T> to resolve to MyObject<T>, whether T is int, string, etc.

Erich Eichinger
05-31-2007, 09:48 PM
Hi,

It's not clear to me what you are trying to do. By doing


Type t = typeof(IMyObject<int>);
string objectName = context.GetObjectNamesForType(t)[0];


you are already asking for a concrete type. Of course you can define an object of type MyObject<int> in your context like described in the reference documentation (http://www.springframework.net/doc-latest/reference/html/objects.html#objects-creation-generic-types):


<object id="myInt" type="MyObject&lt;int>">
...


This will cause the above code to return "myInt" for objectname.

To make the inheritance work, asfaik you need to change your code to

class MyObject<T> : IMyObject<T>
{
...


-Erich

dougknows
05-31-2007, 09:57 PM
Hi Erich,

I'm sorry, I tried to simplify my example and seem to have went a little too far... you are correct that I should have defined my class as:

public class MyObject<T> : IMyObject<T>

Also, there are a few layers between some of this code. I want any of the following to return the concrete implementation:

context.GetObjectNamesForType(typeof(IMyObject<int>))
context.GetObjectNamesForType(typeof(IMyObject<string>))
context.GetObjectNamesForType(typeof(IMyObject<object>))

Is that possible?

If so, I'd actually like the ability to define special generic-type-args where I'd return a different implementation. Assume MyObject2<T> : IMyObject<T>... Is there a way to define that for IMyObject<int> I want to return MyObject2<int>, but for anything else return MyObject<TYPE>?

Erich Eichinger
05-31-2007, 10:18 PM
Hi,

of course this is possible. That's more a less the core functionality of the container ;-).

Just write your configuration like this:

<object id="myInt" type="MyObject&lt;int>" />
<object id="myString" type="MyObject&lt;string>" />
<object id="myFloat" type="MyObject&lt;float>" />
<object id="myObject" type="MyObject2&lt;object>" />


this will cause only requests to typeof(IMyObject<object>) to return a MyObject2<object> instance. In all other cases MyObject<T> will be returned.

-Erich

dougknows
06-01-2007, 01:44 AM
Hi Erich,

The more I'm thinking about it, the more I'm realizing it's probably not possible... :-/ If you're familiar with Castle Windsor, you have the ability to specify when an interface is requested, return TypeX--in that it's not strictly name-based. Since Spring isn't like that, I'm not sure that it's possible I define an open-ended generic.

What I want to account for is the unknown. I want to define any specialized implementations, and then define the default. In the following code, if I request IMyObject<double>, will it return MyObject2<double>?

<object id="myInt" type="MyObject&lt;int>" />
<object id="myString" type="MyObject&lt;string>" />
<object id="myFloat" type="MyObject&lt;float>" />
<object id="myObject" type="MyObject2&lt;object>" />

Erich Eichinger
06-01-2007, 06:14 AM
Hi,

Finally I'm understanding (I hope). Thanks for your patience.

Unfortunately I never worked with Windsor. Can you point me to some quickstart example of this feature?

It definitely seems to be an interesting feature. Can you tell me a bit more about the context of your application where you need this feature? I'd like to know more about your motivation. E.g. why don't you know the type T in advance?

thanks a lot,
Erich

dougknows
06-01-2007, 02:19 PM
Hi Erich,

My department is making the leap from WinForms to a web-based environment, and along with the first web application we're making a framework. I know, most people hear "our framework" and go running in the other direction, but I'm very pleased with it so far.

Anyway, all of our applications have common domain elements that we include in our framework--Buyer, Demographic, Allocation, etc--and we want to include a default repository service for each of them. We're trying to make a way to configure that for any IRepository<T>, return NHRepository<T>... now say in our application that's built on top of our framework, we'd like to not use our NHibernate repository, but instead a TextFileRepository<Buyer> to be returned for cases of IRepository<Buyer>. So I don't need to register IRepository<EVERYTHING_I_MAY_WANT> because IRepository<T> will catch those. Make sense?

Here is an example with Windsor's container of the functionality I'm looking for, under "Registering a generic type without arguments": http://www.castleproject.org/container/documentation/trunk/usersguide/genericssupport.html (http://www.castleproject.org/container/documentation/trunk/usersguide/genericssupport.html)

I realize some people may suggest if Spring doesn't support this, to just use Windsor.. however that's not an option for us. I work for a large organization and everything we use must be approved by "enterprise" architects, and they refuse to look at Castle Windsor because it's never had a full release... so, lucky Spring! :D

dougknows
06-01-2007, 02:23 PM
Also, the reason we don't know all <T>s in advance is because the code that resolves IRepository<T> is in our framework, and we'd like to be able to leave it there, and just for in cases that vary from our default should our application need to make a definition for.

Bruno Baia
06-01-2007, 04:59 PM
Hi,

What we can do easily is something like that :


<object id="myGenericObject" type="MyObject&lt;>" singleton="false" />



IApplicationContext context = ContextRegistry.GetContext();
IMyObject<int> myIntObject = context.GetObject("myGenericObject", new Type[] { typeof(int) }) as IMyObject<int>;


What do you think ?

Bruno

dougknows
06-01-2007, 05:08 PM
That's very close, but I'd like IRepository<T> to be a singleton for the requested instances. I.e. IRepository<Buyer> would be one singleton instance, IRepository<Allocation> would be another singleton instance, etc. Once it resolves all the types involved, it should keep that singleton instance for the specified type and generic arguments. Make sense?

Bruno Baia
06-01-2007, 05:59 PM
Hi,

this is a very specific demand, you should code a helper class on top of Spring for that :


<object id="myGenericObject" type="MyObject&lt;>" singleton="false" />


public T GetGenericObject<T>(string objectNameforDI)
{
IApplicationContext ctx = ContextRegistry.GetContext();
if (!ctx.ContainsObject(typeof(T).FullName))
{
(ctx as AbstractApplicationContext).ObjectFactory.Register Singleton(
typeof(T)..FullName,
ctx.GetObject(objectNameforDI, new Type[1] { typeof(T) }));
}

return ctx.GetObject(typeof(T).FullName) as T;
}


GetGenericObject<MyObject2<int>>("myGenericObject");


GetObject(string name, Type[] genericArgumentTypes) does not exist actually so you can use "Activator.CreateInstance(typeof(T))" instead of "ctx.GetObject(objectNameforDI, , new Type[1] { typeof(T) })" but you can't do DI.


Didn't test, but you can see what I mean,
Bruno

Bruno Baia
06-01-2007, 06:06 PM
I forgot you can use IApplicationContext.ConfigureObject to apply DI :


if (!ctx.ContainsObject(typeof(T).FullName))
{
object obj = Activator.CreateInstance(typeof(T));
obj = ctx.ConfigureObject(objectNameforDI, obj);
(ctx as AbstractApplicationContext).ObjectFactory.Register Singleton(
typeof(T).FullName,
obj);
}



HTH,
Bruno

Erich Eichinger
06-01-2007, 06:12 PM
Hi,

seems like a reasonable solution working right now.

Nevertheless I

a) like the style of Windsor, providing a method

GetObject( Type interfaceType )


b)
like the approach of "partial templates". Maybe it's my strong C++ background ;-)


As I alreaded suggested, a basic mechanism we could build this feature upon would be some kind of "ObjectResolve" handler that is called in case the container can't resolve an object request (similar to AppDomain.AssemblyResolve).

Using this mechanism one can build his own "extensions" for resolving objects.

Just an idea,
Erich

steinard
06-12-2007, 02:51 PM
Hi!

We do something similar by configuring which assemblies a specific interface should look for an implementation in. The assembly mappings are done in the spring container while a normal factory class using reflection does all the work for searching, caching, and returning the type or instantiating it if needed. If the ObjectFactory finds several classes implementing the interface, then an exception is thrown, and the invoker can resolve the exception by choosing the appropriate class from the discovered implementations. Currently, we have organized the code through configuration so that the exception never occurs. Erich helped me out with some custom assembly loading issues a while back, which is related to this case found here (http://forum.springframework.net/showthread.php?t=2498&page=2).

Typically, to create an instance we would write:


IBorrowable book = (IBorrowable)
EnterpriseApp.CreateInstance(typeof(IBorrowableBoo k));


I guess this logic could be extended to work with generics aswell.


/// <summary>
/// Creates an instance of the given type. If the type given is an Interface,
/// then the interface type will be resolved and a type implementing the
/// interface will be instantiated and returned. If the type is a concrete
/// type, then the EnterpriseApp will try to create an instance of the
/// concrete type.
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static object CreateInstance(Type type)
{
if(type.IsInterface) return ObjectFactory.Create(type);
else
{
try { return Activator.CreateInstance(type); }
catch(Exception e)
{
if(log.IsDebugEnabled) log.Debug("Type could not be created: "+type.FullName, e.GetBaseException());
throw;
}
}
}


In the configuration:

<object id="RegisteredAssemblies" type="Viz.Core.Configuration.RegisteredAssemblies, Viz.Core">
<property name="AssemblyList">
<dictionary>
<entry key="Viz.Core.Model" value="Viz.Core.Model.dll"/>
<entry key="Viz.Efm.ServicesImpl" value="Viz.Efm.ServicesImpl.dll"/>
</dictionary>
</property>
<property name="AssemblyMapList">
<dictionary>
<entry key="Viz.Core" value="Viz.Core.Model, Viz.Core.Services"/>
<entry key="Viz.Core.IModel" value="Viz.Core.Model"/>
<entry key="Viz.Core.Model" value="Viz.Core.Model"/>
<entry key="Viz.Efm.IServices" value="Viz.Efm.ServicesImpl"/>
</dictionary>
</property>
</object>


If you look at this line:

<entry key="Viz.Core" value="Viz.Core.Model, Viz.Core.Services"/>

Then this specifies that an interface in the Viz.Core assembly will look for an implementation first in Viz.Core.Model before searching Viz.Core.Services. The first time it will scan through both assemblies to see if several implementations can be found. If only one implementation is located then that type will be cached and instantiated. If you always expect to find many different implementing classes, then you could use one more interface to indicate your context and make your selection based on that information.

Hope this was useful,
Cheers,
Steinar.

Aleksei Kachanov
08-23-2007, 09:12 AM
Hi,

this is a very specific demand, you should code a helper class on top of Spring for that :


<object id="myGenericObject" type="MyObject&lt;>" singleton="false" />


public T GetGenericObject<T>(string objectNameforDI)
{
IApplicationContext ctx = ContextRegistry.GetContext();
if (!ctx.ContainsObject(typeof(T).FullName))
{
(ctx as AbstractApplicationContext).ObjectFactory.Register Singleton(
typeof(T)..FullName,
ctx.GetObject(objectNameforDI, new Type[1] { typeof(T) }));
}

return ctx.GetObject(typeof(T).FullName) as T;
}


GetGenericObject<MyObject2<int>>("myGenericObject");


GetObject(string name, Type[] genericArgumentTypes) does not exist actually so you can use "Activator.CreateInstance(typeof(T))" instead of "ctx.GetObject(objectNameforDI, , new Type[1] { typeof(T) })" but you can't do DI.


Didn't test, but you can see what I mean,
Bruno

This code doesn't work

I have next code


<object id="testObject" type="TestApp.A`1, TestApp" singleton="false" />


Any type of instantiate, raise next exception


IA<int> a = ctx.GetObject("testObject", new Type[] { typeof(A<int>) }) as IA<int>;



IA<int> a = ctx.GetObject("testObject", new Type[] {typeof (int)}) as IA<int>;




IA<int> a = ctx.GetObject("testObject", new Type[] {typeof (IA<int>)}) as IA<int>;


Spring.Objects.Factory.ObjectCreationException: Error creating object with name 'testObject' defined
in 'config [objects]' : Initialization of object failed : Cannot instantiate an open generic type [
TestApp.A`1[T]]. ---> Spring.Util.FatalReflectionException: Cannot instantiate an open generic type
[TestApp.A`1[T]].

How to get object by interface type?

Bruno Baia
09-10-2007, 04:35 PM
Hi,

I've replied here :
How instantiate an open generic type (http://forum.springframework.net/showthread.php?t=3395)

- Bruno