c# - Any clean way for generic parent to get type of child?
-
I have a generic class:
public class PhoenixSingleton<TSingletonType> where TSingletonType : PhoenixSingleton<TSingletonType>,new() { }
And this just looks janky and is annoying me:
class Diagnostics : PhoenixSingleton<Diagnostics> { }
Is there any other way to get the type back into the parent that I'm missing?
-
I've used this pattern many times. I agree, it looks a bit weird, but I don't think there's anything particularly wrong with it.
It's particularly useful when you need to declare methods that use the generic parameter type.
For example:
public interface IStateBase<TState> : where TState : IStateBase<TState> { void UpdateState(Action<TState> stateUpdater); void ReplaceState(TState newState); }
The only other option I can think of is to split out the functionality into a separate class (or separate classes), which might be a good idea if you're violating the single responsibility principle.
The implementation might instead become something like this:
public interface IStateUpdater<TState> : where TState : IStateBase { void UpdateState(Action<TState> stateUpdater); void ReplaceState(TState newState); } public interface IState { } public class MyState : IState { private readonly IStateUpdater<MyState> _stateUpdater; public MyState(IStateUpdater<MyState> stateUpdater) { _stateUpdater = stateUpdater; } }
As you can see, we get rid of this weird generic inception, and asking for an updater for this class looks natural, i.e. the weirdness is gone.
It's difficult to tell if this is appropriate for your situation without more details, but I hope it's given you some food for thought.
-
Do you really need inheritance there?
public class Singleton<T> where T: new() { public static T Instance { get; } = new T(); } class Diagnostics { public static Diagnostics Instance { get { return Singleton<Diagnostics>.Instance; } } }
-
@Gąska said in c# - Any clean way for generic parent to get type of child?:
Do you really need inheritance there?
I guess not, but you are still having to explicitly supply the Type to Singleton there. There are a lot of uses of this Singleton class and I was hoping to get to something like:
class Diagnostics : Singleton
And that would be it. I think the fundamental problem is that the Type of the class using it has to be known to the Singleton so it can return Instance. I can use reflection to get the caller Type but there's no way to then directly return the correct type.
@DoctorJones said in c# - Any clean way for generic parent to get type of child?:
but I hope it's given you some food for thought.
Absolutely, I can see a few places that idiom will be useful. It doesn't directly help me here but thanks!
-
Ok, I'm getting close with:
public class MagicSingleton { private static dynamic _singletonInstance; static readonly object Padlock = new object(); public static dynamic GetInstance() { StackFrame frame = new StackFrame(1); var callingMethod = frame.GetMethod(); var cls = callingMethod.ReflectedType.Name; Type callingType = callingMethod.DeclaringType; lock (Padlock) { return _singletonInstance ?? (_singletonInstance = Activator.CreateInstance(callingType)); } } }
This doesn't work because this gives the class that contains the function that has the method being called, what we need is the class of the instance variable that the method was called on.
-
@Cursorkeys Jesus Christ seriously? Dynamic type for a singleton? Come on.
Won't it screw up Intellisense?
-
@Gąska said in c# - Any clean way for generic parent to get type of child?:
@Cursorkeys Jesus Christ seriously? Dynamic type for a singleton? Come on.
Won't it screw up Intellisense?
Yep, Intellisense stops working. I'm inspecting the executing MSIL right now to see if I can get the instance object that called the function out of it. Just as an academic exercise, these is no way any of this crap can go into production.
I think the straight answer is that there is no way to do what I originally wanted.
Edit: The instance information simply doesn't exist in the MSIL either.
-
Well, there is always a paternity test...
-
@Cursorkeys funny thing - I've figured out how to do what you want while keeping it all type-safe. Instead of writing your class and then instantiating
Singleton
with its type, you turn your class into an anonymous class defined right insideSingleton
's type argument. Obviously that won't work in C#, because it doesn't have anonymous types. You could do that in Java... except you can't actually, because Java cannot create instances of generic parameter (thank you type erasure!) So the method exists, and it's very straightforward - but it can't be done in any existing language, because no language has all the features it requires.
-
@Gąska said in c# - Any clean way for generic parent to get type of child?:
@Cursorkeys funny thing - I've figured out how to do what you want while keeping it all type-safe. Instead of writing your class and then instantiating
Singleton
with its type, you turn your class into an anonymous class defined right insideSingleton
's type argument. Obviously that won't work in C#, because it doesn't have anonymous types.C# does have anonymous types, although they can't be used like that.
-
@Gąska said in c# - Any clean way for generic parent to get type of child?:
You could do that in Java... except you can't actually, because Java cannot create instances of generic parameter (thank you type erasure!)
You make an anonymous concrete subclass of the specialized generic type, which is inspectible and instantiable in the way you want.
-
@dkf said in c# - Any clean way for generic parent to get type of child?:
@Gąska said in c# - Any clean way for generic parent to get type of child?:
You could do that in Java... except you can't actually, because Java cannot create instances of generic parameter (thank you type erasure!)
You make an anonymous concrete subclass of the specialized generic type, which is inspectible and instantiable in the way you want.
You have to draw it out for me. I'm in
Singleton
. I've got type parameterT
. I want to make an instance ofT
insideSingleton
. How do I do that without having to repeatT
's name several times in its own definition?
-
@Gąska IIRC, as a user you write:
singleton = new Singleton<RealClass>() {};
(in a context that needs it). Then inside the constructor for
Singleton
(which probably ought to beabstract
) you get the real class that you're working with withthis.getClass()
and get the type object involved from that withgetGenericSuperclass()
. You can then cast that to aParameterizedType
, and callgetActualTypeArguments()
on the result to obtain the user-provided class.// Sketch of what I mean abstract class Singleton<T> { private final Class userClass; public Singleton() { userClass = (Class) (((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]); } }
There probably needs to be some more decoration on the
userClass
field. I can't be bothered to work it out.Once you've got the user's class, making an instance of it when required should be simple enough, as it involves the less wild and wooly parts of Java's reflection system. (I encountered all this stuff in the Jackson library, where it is used to control serialization. There are probably ways to cache things too.)
It's internally a bit tricky, but not too complicated apart from that one place.
-
-
@Cursorkeys That looks positively awful compared to both your original CRTP-like thing and @Gąska's one-liner.
-
@topspin said in c# - Any clean way for generic parent to get type of child?:
@Cursorkeys That looks positively awful compared to both your original CRTP-like thing and @Gąska's one-liner.
You should have seen the MSIL parsing/injection engine I wrote an hour ago (that didn't work either). I think I should suggest proper anonymous types to the C# working team so that this can be realised. I can't see a solution currently.
-
@Gąska Did you try it? Using this test code:
import java.lang.reflect.*; abstract class Singleton<T> { private final Class<?> userClass; public Singleton() { userClass = (Class) (((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]); } public void print() { System.out.println(userClass); } } class SingletonTest { public static void main(String...args) { Singleton<?> singleton = new Singleton<Integer>() {}; singleton.print(); singleton = new Singleton<Long>() {}; singleton.print(); } }
I got exactly this running in a shell:
bash$ javac SingletonTest.java bash$ java SingletonTest class java.lang.Integer class java.lang.Long
Obviously this means that whether or not it can't work, it's stubbornly actually working anyway. (Also, I'm impressed; that actually worked first time typing blind in
vi
rather than working in an IDE.)
-
@Cursorkeys said in c# - Any clean way for generic parent to get type of child?:
@topspin said in c# - Any clean way for generic parent to get type of child?:
@Cursorkeys That looks positively awful compared to both your original CRTP-like thing and @Gąska's one-liner.
You should have seen the MSIL parsing/injection engine I wrote an hour ago (that didn't work either).
The lengths developers will go to to save a couple key strokes...
-
@dkf said in c# - Any clean way for generic parent to get type of child?:
@Gąska Did you try it?
Of course not. But this code defies everything I know about Java's generics (and I did semi-extensive research just before writing my post). What JRE version are you using?
-
@Cursorkeys said in c# - Any clean way for generic parent to get type of child?:
I can't see a solution currently.
I can't see a problem with the initial code.
-
@loopback0 said in c# - Any clean way for generic parent to get type of child?:
@Cursorkeys said in c# - Any clean way for generic parent to get type of child?:
I can't see a solution currently.
I can't see a problem with the initial code.
I know, it's just a few more keypresses. But I was convinced this morning that I was obviously missing something and there would be a much better way to do it.
-
@Gąska said in c# - Any clean way for generic parent to get type of child?:
What JRE version are you using?
As it happens, 10. () I believe from the docs that it's supposed to work from 5 onwards, which is everyone except really retarded old IBM code.
The reason it works is that the anonymous inner classes inside
SingletonTest.main
are concrete (though trivial) classes. They know that they're not just aSingleton
subclass, but aSingleton<Integer>
orSingleton<Long>
subclass. It doesn't work if you don't use a real type there; the type parameter has to be known in its entirety at compile time.
-
@Cursorkeys said in c# - Any clean way for generic parent to get type of child?:
I think I should suggest proper anonymous types to the C# working team so that this can be realised. I can't see a solution currently.
Hopefully they'll tell you to just let your damn DI container manage singletons. This whole thing seems like a solution in search of a problem.
-
@Gąska said in c# - Any clean way for generic parent to get type of child?:
@Cursorkeys funny thing - I've figured out how to do what you want while keeping it all type-safe. Instead of writing your class and then instantiating
Singleton
with its type, you turn your class into an anonymous class defined right insideSingleton
's type argument. Obviously that won't work in C#, because it doesn't have anonymous types. You could do that in Java... except you can't actually, because Java cannot create instances of generic parameter (thank you type erasure!) So the method exists, and it's very straightforward - but it can't be done in any existing language, because no language has all the features it requires.I'll do you one better. Swift to the rescue!
protocol Singleton { static var instance: Self { get } } class Diagnostics: Singleton { static let instance: Diagnostics = new Diagnostics() private init() {} } func foo<T: Singleton>() { let instance = T.instance }
-
@pie_flavor said in c# - Any clean way for generic parent to get type of child?:
class Diagnostics: Singleton {
static let instance: Diagnostics = new Diagnostics()Um...
-
@Gąska um what?
-
@pie_flavor um you haven't solved the "problem".
-
@Gąska the solution to the example illustrated the solution to the problem. A protocol has access to the
Self
type (as do classes, but only for return types), functioning as it does in Rust - it resolves to the type of whatever is implementing it.
-
@pie_flavor the "problem" was "I only want to write the class name once". Your "solution" doesn't solve it. Also, the
Singleton
protocol is quite useless and doesn't actually help in achieving singleton behavior.
-
@Gąska said in c# - Any clean way for generic parent to get type of child?:
the "problem" was "I only want to write the class name once
First world coding problems.
-
@Gąska why does it not help in achieving Singleton behavior? It mandates a public static self-typed getter named instance
-
@pie_flavor OK, it helps in that it yells at you if you forget to implement the singleton for a class marked as a singleton. But it doesn't change the way it's to be implemented. You could remove it and the rest of code would stay identical. And also the reverse - the code needed to implement it is identical with or without that protocol.
-
@Gąska yes, that's generally how interfaces work. Your point?
-
@Gąska oh, did you mean something like this?
private var typeMap: Dictionary<Any.Type, Any> = [:] protocol Singleton { init() } extension Singleton { var instance: Self { get { var instance = typeMap[Self.self] if instance == nil { instance = new Self() typeMap[Self.self] = instance } return instance! as Self } } } class Diagnostics: Singleton {} func foo() { doThing(with: Diagnostics.instance) }
-
@pie_flavor yes, this is much better. Though I don't particularly like the runtime map of types to objects. Any way to move it to compile time? Dunno, a compile-time generic method with static variable?
@pie_flavor said in c# - Any clean way for generic parent to get type of child?:
@Gąska yes, that's generally how interfaces work.
Well, yes, technically yes. But interfaces are usually (always?) created to allow polymorphism. You rarely (never?) need to be polymorphic over different singleton types (especially since, with the protocol as you've written, the only thing you can do is get an instance that doesn't have any non-static members exposed in the protocol).
-
I suppose something like this doesn't count, even though it technically satisfies your requirement of only writing the class name once in its definition.
...except you then pay for it each time when you invoke it XD
...but then again you could name the function "_" instead of Instance, which would again almost equalize the typing length, and since the manager is named Singleton, it would still be pretty clear.oh, and also it fails the basic requirement of private constructor, which...
public class SingTest2 : ISingleton { private DateTime n; public SingTest2() { n = DateTime.Now - TimeSpan.FromDays(365); } public string DoStuff() { return "This is weird: " + n.ToString(); } } //yes, this is only a tag to notify you there's something different about the class, technically this thing can singletonize anything public interface ISingleton { }; public static class Singleton { private static Dictionary<Type, Object> _instances = new Dictionary<Type, object>(); public static T Instance<T>() where T : ISingleton, new() { if (!_instances.ContainsKey(typeof(T))) _instances.Add(typeof(T), new T()); return (T)_instances[typeof(T)]; } }
yeah, i know, completely stupid, but i just spent two hours trying everything i could think of including things which halfway towards the solution contained something I thought "this won't work the way I assume", but they did, and... failed a bit later. anyway, so I'll be dambed if I don't post my useless stupid bit into these useless stupid musings!
(wouldn't it be amazing if there could be at least static class indexers? and on that note, "self" in general?)
or at least static extension methods?
or... whatever... actually, it's probably best that a language doesn't have features which you start wishing for when you're trying to do something utterly useless and stupid out of sheer laziness XD
-
@Gąska you can't declare a generic variable, no. Either the parent class stores it in a dictionary or each child class stores it themselves in a static variable.
-
@pie_flavor said in c# - Any clean way for generic parent to get type of child?:
@Gąska you can't declare a generic variable, no.
But you can declare static local variable in a generic function, can you?
-
@Gąska what the hell is a static local variable?
-
@sh_code You could make the constructor private and have
Singleton
invoke it via reflection.
-
@pie_flavor said in c# - Any clean way for generic parent to get type of child?:
@Gąska what the hell is a static local variable?
(It doesn't look like it but it links to "static local variables" section.)
-
@Gąska I'm pretty sure generics aren't meant to be used like that and the only reason you can do it in C++ is because templates are already Turing-complete.
-
@pie_flavor you're wrong on both counts.
-
-
@error I could, if I was willing to go into THAT realm of disgustingness... =D
I even thought about it for a bit, but then I decided "no, even I am not THAT perverted"
-
(yeah, i'm being all offensive and rude, but in a comical, well-meant tone of voice. don't take it as a personal attack)
let's break this one down:
static locallocal: belonging to the current (most-low-level) scope
static: belonging to the class definition itself (as opposed to the instance)so, static local - most-low-level scope belonging to the class definition. therefore, belonging to the class definition. therefore, static local is the same as static.
which I assume is not what you had in mind. so what the fuck did you have in mind?
you know... it's useful to think about static variables as "superglobals", a level above globals, since that's how they behave. so again, your proposition makes no sense. local superglobal? local variable of which single value belongs globally to all instances of that class?
...WAT.
no i haven't read the link. if you're going to defend this idea, you're going to have to do it with your own words and logicks (sic), because I seriously want to see that.
-
I had a similar chuckle when I read
static dynamic
elsethread.
-
I actually suggested static local variables as a feature to the TypeScript team and they shot it down ruthlessly.
I still think they'd be handy but I agree now that they've decided to align themselves closer to ECMAScript.
-
@error said in c# - Any clean way for generic parent to get type of child?:
I had a similar chuckle when I read
static dynamic
elsethread.
-
@sh_code said in c# - Any clean way for generic parent to get type of child?:
let's break this one down:
static locallocal:
belonging to the current (most-low-level) scopefunction-scoped
static:belonging to the class definition itself (as opposed to the instance)lives foreverso, static local -
most-low-level scope belonging to the class definition. therefore, belonging to the class definition. therefore, static local is the same as static.a function-scoped variable that lives forever - in other words, one that persists (keeps its value) between calls to the containing function.FTFY. Now add the fact that templates are basically automated copy-paste and it all starts making sense.