As usual, I wrote my life story explaining the leadup to my issue before getting to the question. I’ll post my question without the long winded explanation, with the assurance that yes, this complicated way I’m going about this does seem to be the way to go, although I am open to other options/ideas.
I need to reference a static method or property on a class, inside a generic function that processes lots of different classes. The problem that I’m running into is that you cannot use a static method/property with an Interface.
So I thought about a class that implemented the static method, and have the classes that need this functionality inherit from it. Problem is you cannot override a static method. Or the parent class where the static method exists cannot call a static method in it’s derived class - it has no concept that it’s there. (Unless someone knows how to make that work?)
The lazy method is to implement a switch/case in the generic function that processes the class, and every time I add a new class that needs this functionality, update the case statements. That’s clunky and makes my skin crawl. I want an elegant self contained option. New class? Forced to implement some methods that the generic function just knows are there. But I can’t, because it has to be static.
How can I call a method, inside a class, that likely has to be static so that it can be called without an instance member of that class available, when neither interfaces or inheritance lets me work with a static method?
While this will likely get a bunch of “why the hell are you doing it the hard way?” replies, trust me, for what I’m doing this is the best way. I think. I’m open to other suggestions. My main goal is write this code then never have to touch the generic function again, regardless of what new classes implement it.
How performant does this have to be? If you want to get really hacky, you could do it with reflection, but the performance will not be amazing.
Yeah I thought about reflection but I’d rather not if I can avoid it. It’s only used (right now anyway) when the app boots up. The point is to scan objects that already exist locally, get the most recent timestamp, then use that in an API call (only pulling down records that have been updated since the last boot of the app).
The issue is that the generic function doesn’t know WHAT to scan to find the dates. It knows it’s running the API calls for the MaterialData class. But without a switch/case statement I’d need to update every time a new class was added to the functionality, it doesn’t know where to look to get that info. If it could run a static method inside MaterialData, it could. But then there’s the issue with statics in interfaces and inheritance.
And the function, when called, doesn’t know about any (possibly existing, possibly not) instances of the class.
And reflection is just too hacky for my tastes. Too much chance for something to break. With the compiler validating everything is cool, I cannot accidently forget to implement something the reflection is depending on.
But if reflection is the ONLY way, I might be stuck with it.
There is no way to do this with generics. Keep in mind that the code of generic methods has to be precompiled and has to work with any type that is allowed by the constraints. Static members are just that, they are statically linked. The code literally has a direct link to the piece of code that should execute. Of course you can not override static members because dynamic dispatch requires a vtable which requires an instance.
If you want the code to automatically work with newly added types, you have to use reflection. However reflection doesn’t have to be slow. What you should do is scan all types at start up that are relevant (you talked about an interface, so all types implementing this interface would be a good criteria). You use reflection to get the MethodInfo for that static method for each type. Now you can create a delegate of that methodInfo using Delegate.CreateDelegate. Finally you can use a static Dictionary<System.Type, System.Action>
to store all those methods for later use. When you need to call those methods you can simply look it up in the dictionary and directly call it. A delegate call is almost the same as a direct method call. It also avoids the boxing of arguments or return values if that’s even required in your case.
2 Likes
Thanks Bunny83, that’s a genius solution. I’m going to google it now because it’s new to me, but if you see this early enough and it’s not too much trouble, could you point me in the right direction to scan all the types at startup? Not sure how to do that (yet). Just a matter of me not knowing what class or method would get me a list of all classes that exist in the project that implement a specific interface.
Also, in Unity, is there a good/preferred place to put code that should be run at startup? Or just pop it into a monobehaviour somewhere and set the script execution order so it executes first? Thats what I’m currently doing, but I set that up ages ago when I first started using Unity, and havent had to implement anything at startup since then. Realized that I never did look into if there was a preferred place to put stuff that absolutely should be run first, or if what I did is the best way.
Thanks again for that tip.
Edit: I’ll post an update here if/when I figure out the ‘scan all types for classes that have my interface’ so no one wastes time writing up an explanation if I managed to figure it out myself first.
Well that was fast. First google link I hit had this snippet and appears to do exactly what I need to gather all classes that implement an interface. It probably needs tweaking, but it’s a good base:
public List<string> GetAllEntities()
{
return AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypes())
.Where(x => typeof(IDomainEntity).IsAssignableFrom(x) && !x.IsInterface && !x.IsAbstract)
.Select(x => x.Name).ToList();
}
1 Like