- Home /
Writing an extension method for a class. Is it possible? How?
Hi,
I wrote this piece of code to speed up checking existance of singletons without actually changing the singletons implementation and without of course checking their Instance property or the non-accessible _instance private static variable.
using UnityEngine;
using System.Collections;
public static class FNSUtils {
public static bool ExistsInSceneOrError(this System.Type type)
{
if (GameObject.FindObjectOfType(type) != null)
return true;
else
{
Debug.LogError("Couldn't find a " + type.ToString() + " in the scene. Add it, save the scene and reload it");
return false;
}
}
}
In this way I could simply do a check like GameManager.ExistsInSceneOrError() and return a value, without the hassle to write this for every Manager or Singleton.
But, it doesn't seem to work.
Now, is there a way to do this with the method extensions? Which other solution would you propose? A static utility class with a static method?
I assume you've tried: GameObject.FindObjectOfType(typeof(type))
Sure, the problem is not the feasibility, but the need to provide a generic and easy to use shortcut to that, since I also have to do some checks.
It's a piece of code I'd have to rewrite over and over.
Calling myClass.ExistsInSceneOrError() is much cleaner.
Any idea on how to make it work? Am I missing something?
"But, it doesn't seem to work." Need more details. Looks ok to me, at fist glance. Is it the FindObjectsOfType that fails?
Alternatives: Create a Static Class that contains a Dictionary - and have your singletons register themselves in there. Then when you need to check for existence, it should be a much shorter list to look through than all scene objects, and you can just use dictionary.Contains to see if it exists. Though I'd also return the looked-up gameobject or null, rather than a bool.
Thanks, but as I said, I'm not looking for alternative solutions, I'm already using one in the meantime.
Nothing fails, the compiler works just as fine as long as I don't try to use it.
Simply, when typing the name of any class and pressing period, the autocomplete fails to suggest me the extension method, and typing it results in a "class doesn't contain such field or method" compile error.
i know you said no alternatives, but it won't work with an extension method and a faster way would be a generic singleton with that method at its core using T
Answer by Glurth · Dec 12, 2016 at 05:00 PM
Well, the way you have it defined now, no ..
SomeClass.ExistsInSceneOrError()
will NOT give you access to this function. Note, you are NOT extending "SomeClass", you are extending System.Type. However,
typeof(SomeClass).ExistsInSceneOrError()
SHOULD work, because the typeof() function will return a Systsem.Type variable.
Consider the extension method:
static void func(this Object o)
Object o, must be an INSTANCE of a variable. There is no way to specify a TYPE as input in a regular parameter. Sure, you can use System.Type, but this will ALSO require and instance of a variable (of class System.Type), (which in the code above, is instantiated by typeof() ).
Extension methods are simply not meant to provide the syntax you are looking for BECAUSE they take as input; variable instances, not types.
Instead of Extension function, you could use Generics and Inheritance to get the syntax you want:
public abstract class SingletonBase<T>:MonoBehaviour where T: SingletonBase<T>
{
public static bool ExistsInSceneOrError()
{
if (GameObject.FindObjectOfType(typeof(T)) != null)
return true;
else
{
Debug.LogError("Couldn't find a " + typeof(T).ToString() + " in the scene. Add it, save the scene and reload it");
return false;
}
}
}
class SingletonOne : SingletonBase<SingletonOne> //formerly :Monobehavior
{
//no change
}
class SingletonTwo : SingletonBase<SingletonTwo> //formerly :Monobehavior
{
//no change
}
usage:
SingletonTwo.ExistsInSceneOrError();
You're right! It works! Thanks!
I actually don't need it to work on System.Type but on a generic class (even if I have to use it on $$anonymous$$onoBehaviour only).
Would that change the calling syntax to avoid that ugly typeof?
What would you change to make it work on a generic class or $$anonymous$$onobehaviour? Would it still be possible?
I think the way you probably want to go is with a simple Generic function(https://msdn.microsoft.com/en-us/library/twcad0zb.aspx), rather than extension function:
public static bool ExistsInSceneOrError<T>()
{
if (GameObject.FindObjectOfType(typeof(T)) != null)
return true;
else
{
Debug.LogError("Couldn't find a " + typeof(T).ToString() + " in the scene. Add it, save the scene and reload it");
return false;
}
}
Call using
FNSUtils.ExistsInSceneOrError< SomeClass >();
edit/addition: I've only used it on generic CLASSES myself, so I don't know if the following works for generic functions too. You can limit availability of your T parameter, to only certain classes, that fullfill particular requirements, by using the WHERE keyword: https://msdn.microsoft.com/en-us/library/d5x73970.aspx
e.g. to make it work ONLY on monobehaviors
public static bool ExistsInSceneOrError<T>() where T: $$anonymous$$onobehavior
TESTED: this DOES work- and will make the compiler generate an error if we now try to call say... FNSUtils.ExistsInSceneOrError< int >();
since "int" is not a $$anonymous$$onobehavior
Generics are a nice idea.
So I tried to change the extension using generics as well (this is definitely a thing I've never tried before):
public static bool ExistsInScene<T>(this T t)
{
if (GameObject.FindObjectOfType(typeof(T)) != null)
return true;
else
{
Debug.LogError("Couldn't find a " + typeof(T).ToString() + " in the scene. Add it, save the scene and reload it");
return false;
}
}
but of course I was expecting some weird syntax, but in the end I still have to call it as
typeof(class).ExistsInscene();
I'm probably missing something
Again, I tried:
public static bool ExistsInScene<T>(this T t) where T:Object
{
if (GameObject.FindObjectOfType(typeof(T)) != null)
return true;
else
{
Debug.LogError("Couldn't find a " + typeof(T).ToString() + " in the scene. Add it, save the scene and reload it");
return false;
}
}
but the calling syntax would be:
(FNSScene$$anonymous$$anager as Object).ExistsInScene()
now if I only was able to use some keyword to force typecasting in the fields of the method, such as "coerce", used in other languages...
FYI: updated answer with an example to generate the syntax you are looking for.
Thanks, but the premise was to use the extension system without touching the singleton code, as per OP.
Definitely, implementing it this way was the most straightforward method to have that syntax working.
Answer by JoshuaMcKenzie · Dec 15, 2016 at 08:32 AM
You can use Extension Methods just fine. its definitely possible.
public interface ISingleton{} // marker interface
public static class SingletonExtensions
{
private static Dictionary<System.Type,UnityEngine.Object> singletons = new Dictionary<System.Type, UnityEngine.Object>();
public static bool SingletonExists<T>(this Component component)
where T:UnityEngine.Object
{
return GetSingleton<T>()!=null;
}
public static T GetSingleton<T>(this MonoBehaviour script)
where T:UnityEngine.Object
{
T singleton = GetSingleton<T>();
if(singleton == null)
{
Debug.LogErrorFormat(script,"Singleton {0} not found!",typeof(T));
}
return singleton;
}
public static T GetSingleton<T>(this ScriptableObject script)
where T:UnityEngine.Object
{
T singleton = GetSingleton<T>();
if(singleton == null)
{
Debug.LogErrorFormat(script,"Singleton {0} not found!",typeof(T));
}
return singleton;
}
public static void LoadSingleton<T>(this ISingleton script)
where T:UnityEngine.Object,ISingleton
{
T singleton = GetSingleton<T>();
if(singleton == null)
{
Debug.LogErrorFormat(script,"Singleton {0} not found!",typeof(T));
return;
}
script = singleton;
}
private static T GetSingleton<T>()
where T:UnityEngine.Object
{
UnityEngine.Object singletonObject = null;
System.Type type = typeof(T);
singletons.TryGetValue(type, out singletonObject);
if(singletonObject != null) return (T) singletonObject;
T singleton = UnityEngine.Object.FindObjectOfType<T>();
if(singleton == null) return null;
singletons[type] = singleton;
return singleton;
}
}
You can also have it reference the singleton itself as the caller. a little known fact about extention methods is that the instance running the extension can be null so something like this
ISingleton gameController = null;
gameController.LoadSingleton<GameController>();
can work even if gameController was null when the extension method was called.
the question of "Should you use Extension methods for this?" however is debatable.
I definitely agree I should be using other ways to accomplish this (and I actually am using a good compromise), but I thought it would still be an interesting subject to have an answer for :) Or at least brainstorm a bit around it. I think corner cases are always interesting to explore.
Answer by jenci1990 · Dec 15, 2016 at 07:07 PM
Your code is good. Try use like this:
public static class FNSUtils {
public static bool ExistsInSceneOrError(this System.Type type) {
if (GameObject.FindObjectOfType(type) != null)
return true;
else {
Debug.LogError("Couldn't find a " + type.ToString() + " in the scene. Add it, save the scene and reload it");
return false;
}
}
}
And use:
typeof(AnyClassName).ExistsInSceneOrError();
Uhm, you just copied the original code into your answer. How does that answer the question?
If you just wanted to show how to use it, you're 3 days late. Glurth already showed that.