I have the base class derived from MonoBehaviour
public abstract class AbstractMyGameBeh : MonoBehaviour{//....}
And many other classes, realising this base class.
public class MyGameBeh: AbstractMyGameBeh {//....}
public class BMyGameBeh: AbstractMyGameBeh {//....}
public class CMyGameBeh: AbstractMyGameBeh {//....}
So now I need to find all gameobjects of this classes.
Ofcource, I can do like this, but if I make more classes derived from AbstractMyGameBeh, I'll be have to fix this code part again.
GameObject.FindObjectsOfType<AMyGameBeh>()
GameObject.FindObjectsOfType<BMyGameBeh>()
GameObject.FindObjectsOfType<CMyGameBeh>()
C# 6.0 allows the use of
typeof
if(objOfTypeMyGameBeh.GetType() == typeof(AbstractMyGameBeh))
{
//It's the type you want
}
else
{
//it's not the type you want
}
or as
if(objOfTypeMyGameBeh as AbstractMyGameBeh != null)
{
//It's the type you want, works aswell with inheritance
}
else
{
//it's not the type you want
}
That should work and you can put it in a loop pretty easily to check every object.
As pointed out in the comments, if you're not storing the value you can use is keyword
I've had a similar Problem recently and this solution might be applicable to your problem:
public static IEnumerable<Type> GetTypes()
{
var sourceAssembly = Assembly.GetCallingAssembly();
var assemblies = new List<Assembly>();
assemblies.AddRange(sourceAssembly.GetReferencedAssemblies().Select(an => Assembly.Load(an)));
assemblies.Add(sourceAssembly);
var subclassTypes = new HashSet<Type>();
foreach (var assembly in assemblies)
{
var types = assembly.GetTypes().Where(t => t.IsSubclassOf(typeof(AbstractMyGameBeh)));
foreach (var type in types) subclassTypes.Add(type);
}
return subclassTypes;
}
This should work even if your AbstractMyGameBeh is in a different Assembly as long as you call it from an assembly where all child classes are available.
It will search all assemblies (the calling one and all that are referenced by it) for heir classes of your abstract class.
Now This leaves you with a set of Types. So you'd still need to use reflection to call GameObject.FindObjectsOfType<T>(). Should be something like this...
foreach(var subclassType in subclassTypes)
{
MethodInfo method = GetType("GameObject").GetMethod("FindObjectsOfType")
.MakeGenericMethod(new Type[] { subclassType });
method.Invoke(gameObject, new object[] { });
}
Related
I have an interface:
public interface IPath
{
// Some method
}
and I have two classes which are inheriting this interface
public class First : IPath { }
public class Second: IPath { }
By the way, in some method I need to choose which class to use, First or Second, it depending on one string property (type), which I get from database. It looks like:
public void SomeMethod(string type)
{
if (type == "First") { // creating instance of First class }
else if (type == "Second") { // creating instance of Second class }
else { ... }
}
Question is: how can I avoid if/else or switch/case constructions and automaticly create the right instance, depending on the string variable?
You could create a dictionary to map from string to Type and the use that Type and Activator.CreateInstance to create an instance of that type.
Alternatively you could fetch the type using reflection and not need a dictionary at all
private Dictionary<string, Type> _iPathMapping = new Dictionary<string, Type>
{
{ nameof(First), typeof(First) },
{ nameof(Second), typeof(Second) },
};
// ...
public IPath Create(string path)
{
var type = _iPathMapping[path];
return (IPath) Activator.CreateInstance(type);
}
(You'd want to extend that code with safety checks, see below)
But this is fundamentally a bad solve. The problem to this is that it's harder to pass parameters to constructors and it's unsafe as well, if any of your implementations don't have a parameterless constructor, this will fail, but not with a compiler error, no it will fail during runtime, i.e once a user (or hopefully testers/ automatic tests) ran into the problem. So a better way would be to store a method that's invoked to construct the type instead of using Activator.CreateInstance, something like
private Dictionary<string, Func<IPath>> _iPathMapping = new Dictionary<string, Func<IPath>>
{
{ nameof(First), () => new First() },
{ nameof(Second), () => new Second() },
};
// ...
public IPath Create(string path)
{
if (_iPathMapping.TryGetValue(path, out var func))
return func.Invoke();
return null;
}
This solves the problem of parameters for the constructor in that it doesn't throw a runtime exception.
But we still haven't solved the problem of actually passing parameters to the constructor, especially when First and Second require different parameters. The only clean* way to I can think of to handle this in a generic and reusable way is using a Dependency Injection framework/ context to actually construct our instances.
But in general, the if/ else if chain or switch statement isn't necessarily a bad thing, you even see it in some places inside .NET
* You could replicate the part of a DI framework that's responsible for resolving dependencies for a constructor, but that's just re-implementing the wheel and might as well save the effort needed and just pull in a dependency like Microsoft.Extensions.DependencyInjection
I have a shorter version as answer, but I saw "MindSwipe" already offered you one:
Dictionary<string, Type> map = new Dictionary<string, Type>();
map.Add("First", typeof(First));
map.Add("Second", typeof(Second));
var instance = Activator.CreateInstance(map[<your parameter as string>]);
I am working with the Dapper.FluentMap library, and trying to set up auto-registration of my mapping classes. To do that, I need to call FluentMapConfiguration.AddMap<T>(EntityBase<T>) for each of the mapping classes.
I can do it like this:
public class TypeAMap : EntityMap<TypeA> {}
public class TypeBMap : EntityMap<TypeB> {}
public class TypeCMap : EntityMap<TypeC> {}
public void Register(FluentMapConfiguration configuration)
{
configuration.AddMap(new TypeAMap());
configuration.AddMap(new TypeBMap());
configuration.AddMap(new TypeCMap());
// I have a hundred of these, you can see where I'm going...
}
Obviously a problem in the making when you forget to register a map and wonder why your data isn't loading properly. So on to some reflection to auto-register:
public void Register(FluentMapConfiguration configuration)
{
var maps = GetType().Assembly.GetExportedTypes().Where(t =>
!t.IsAbstract &&
!t.IsInterface &&
t.BaseType is { IsGenericType: true } &&
t.BaseType.GetGenericTypeDefinition() == typeof(EntityMap<>)
).ToArray();
foreach (var map in maps)
{
var baseType = typeof(EntityMap<>);
var typeArguments = map.BaseType.GetGenericArguments();
var genericType = baseType.MakeGenericType(typeArguments);
var instance = Activator.CreateInstance(genericType);
configuration.AddMap((dynamic) instance);
}
}
but when it gets to the call to Activator.CreateInstance, it fails, with a MissingMethodException, Cannot create abstract class. It looks like it's trying to create an instance of EntityBase<TypeA> rather than TypeAMap, and since EntityBase<T> is an abstract class, I'm getting the error. So how can I construct my instances correctly?
The Where call filters out the types in the assembly and includes only types that are not abstract, not interfaces, and are direct subclasses of EntityMap<anything>. So maps contains the types TypeAMap, TypeBMap, TypeCMap etc.
Then for each of those, your code gets its base class and tries to instantiate that instead. See the annotated code in the for loop:
// suppose "map" is typeof(TypeAMap)
var baseType = typeof(EntityMap<>);
// typeArguments would be an array containing typeof(TypeA) only.
var typeArguments = map.BaseType.GetGenericArguments();
// genericType would be typeof(EntityMap<TypeA>)
var genericType = baseType.MakeGenericType(typeArguments);
// now you try to instantiate a EntityMap<TypeA>, and fails, because EntityMap<TypeA> is abstract
var instance = Activator.CreateInstance(genericType);
Since you want to do the reflection version of new TypeAMap(), new TypeBMap() etc, and we know that maps contain those types that you want to instantiate, you can just do:
foreach (var map in maps)
{
var instance = Activator.CreateInstance(map);
configuration.AddMap((dynamic) instance);
}
You don't need to care about the generic type parameters for EntityMap at all. The dynamic binder will figure that out when it tries to infer the type parameter for AddMap.
Background:
I am working with an organization that has an ever-growing collection of data types that they need to extract data from. I have no ability to change these data types. Some are machine-generated from XML files provided by other organizations; some are controlled by intransigent internal dev teams; and some are so old that no one is willing to change them in any way for fear that it will destabilize the entire Earth and cause it to crash into the sun. These classes don't share any common interface, and don't derive from any common type other than object. A few sample classes are given below for illustration purposes:
public class Untouchable
{
public string Data;
}
public class Unchangeable
{
public int Info;
}
The good news is that most of the time, I can use canned functionality to get at least some of the data from instances of the various classes. Unfortunately, most of these classes also have weird and specialized data that needs class-specific logic to extract data from. Also, information often needs to persist inside of the data extractors because the data objects I'm pulling data from have "interactions" (don't ask).
I have created an abstract generic Extractor<T> class to serve as a repository of common methodology, and an IExtractor<T> interface to serve as a convenient handle to access functionality. I also have a few specific (de-generic?) implementations of this class that can extract information from the business objects built from some of the data types. Here's some sample code to illustrate:
public interface IExtractor<T>
{
string ExtractionOne(T something);
string ExtractionTwo(T something);
}
public abstract class Extractor<T> : IExtractor<T>
{
private string internalInfo; // Certain business logic requires us to keep internal info over multiple objects that we extract data from.
protected Extractor() { internalInfo="stuff"; }
public virtual string ExtractionOne(T something)
{
return "This manipulation is generally applicable to most conceivable types.";
}
public abstract string ExtractionTwo(T something); // This DEFINITELY needs to be overridden for each class T
}
public class UntouchableExtractor : Extractor<Untouchable>
{
public UntouchableExtractor() : base() { }
public override string ExtractionTwo(Untouchable something)
{
return something.Data;
}
}
public class UnchangeableExtractor : Extractor<Unchangeable>
{
public UnchangeableExtractor() : base() { }
public override string ExtractionTwo(Unchangeable something)
{
return something.Info.ToString();
}
}
I don't yet support all of the available data types, but management wants to roll out the data extractor to end-users using a command-line interface. They're telling me that we should start extracting the data we can extract, and get to the rest later. Support for the many unmodifiable types will be added by me and by and other programmers as time permits, and the end-users are expected to work around our latency. This actually makes sense in our real-world setting, so just go with it.
The Problem:
The list of data types that we want to pull information from is very large. Maintaining an explicit list of supported types in code will be tricky and error prone -- especially if we find any problems with specific data extractors and need to revoke support until we fix some bugs.
I would like to support the large and changing list of supported data types from a single entry point that dynamically determines the "right version" of IExtractor<> to use based on a passed in dynamic dataObject. If there is no class that implements the IExtractor<> to support the given dataObject, than an error should be thrown.
What Doesn't Work:
I tried taking a dynamic thing and using typeof(Extractor<>).MakeGenericType(thing.GetType()) to create an instance of Extractor<Untouchable> or Extractor<Unchangeable>, but those are abstract classes, so I can't use Activator.CreateInstance() to build an instance of those classes. The core issue with this approach is that it's unfortunately looking for a class of the form Extractor<> instead of an interface of the form IExtractor<>.
I considered putting extension methods like IExtractor<T> BuildExtractor(this T something) in some class, but I'm nervous about running into some business logic called BuildExtractor that already exists in one of these untouchable classes. This may be an unhealthy level of paranoia, but that's where I'm at.
Where I need help:
I'd welcome any suggestions for how I can create a single entrypoint for an unconstrained collection of classes. Thanks in advance.
Combining some code from StackOverflow and my own testing, I suggest using Reflection to find all types implementing an interface:
public static class TypeExt {
public static bool IsBuiltin(this Type aType) => new[] { "/dotnet/shared/microsoft", "/windows/microsoft.net" }.Any(p => aType.Assembly.CodeBase.ToLowerInvariant().Contains(p));
public static IEnumerable<Type> ImplementingTypes(this Type interfaceType, bool includeAbstractClasses = false, bool includeStructs = false, bool includeSystemTypes = false, bool includeInterfaces = false) =>
AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(a => a.GetLoadableTypes())
.Distinct()
.Where(aType => (includeAbstractClasses || !aType.IsAbstract) &&
(includeInterfaces ? aType != interfaceType : !aType.IsInterface) &&
(includeStructs || !aType.IsValueType) &&
(includeSystemTypes || !aType.IsBuiltin()) &&
interfaceType.IsAssignableFrom(aType) &&
aType.GetInterfaces().Contains(interfaceType));
}
public static class AssemblyExt {
//https://stackoverflow.com/a/29379834/2557128
public static IEnumerable<Type> GetLoadableTypes(this Assembly assembly) {
if (assembly == null)
throw new ArgumentNullException("assembly");
try {
return assembly.GetTypes();
} catch (ReflectionTypeLoadException e) {
return e.Types.Where(t => t != null);
}
}
}
Reflection can be quite slow, and in my testing, getting all loaded types was the slowest part, so I added caching of the loaded types and the implementing types, but this does mean you will need to refresh the loaded types if you dynamically load assemblies:
public static class TypeExt {
public static bool IsBuiltin(this Type aType) => new[] { "/dotnet/shared/microsoft", "/windows/microsoft.net" }.Any(p => aType.Assembly.CodeBase.ToLowerInvariant().Contains(p));
static Dictionary<Type, HashSet<Type>> FoundTypes = null;
static List<Type> LoadableTypes = null;
public static void RefreshLoadableTypes() {
LoadableTypes = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetLoadableTypes()).ToList();
FoundTypes = new Dictionary<Type, HashSet<Type>>();
}
public static IEnumerable<Type> ImplementingTypes(this Type interfaceType, bool includeAbstractClasses = false, bool includeStructs = false, bool includeSystemTypes = false, bool includeInterfaces = false) {
if (FoundTypes != null && FoundTypes.TryGetValue(interfaceType, out var ft))
return ft;
else {
if (LoadableTypes == null)
RefreshLoadableTypes();
var ans = LoadableTypes
.Where(aType => (includeAbstractClasses || !aType.IsAbstract) &&
(includeInterfaces ? aType != interfaceType : !aType.IsInterface) &&
(includeStructs || !aType.IsValueType) &&
(includeSystemTypes || !aType.IsBuiltin()) &&
interfaceType.IsAssignableFrom(aType) &&
aType.GetInterfaces().Contains(interfaceType))
.ToHashSet();
FoundTypes[interfaceType] = ans;
return ans;
}
}
}
public static class AssemblyExt {
//https://stackoverflow.com/a/29379834/2557128
public static IEnumerable<Type> GetLoadableTypes(this Assembly assembly) {
if (assembly == null)
throw new ArgumentNullException("assembly");
try {
return assembly.GetTypes();
}
catch (ReflectionTypeLoadException e) {
return e.Types.Where(t => t != null);
}
}
}
Once you have one of these, you can make a factory method that takes a dynamic object:
public static class ImplementingFactory {
public static Type ExtractorType(dynamic anObject) {
Type oType = anObject.GetType();
var iType = typeof(IExtractor<>).MakeGenericType(oType);
var ans = iType.ImplementingTypes().FirstOrDefault();
if (ans == null)
throw new Exception($"Unable to find IExtractor<{oType.Name}>");
else
return ans;
}
}
The following snippet will create the concrete instance of Extractor<T> class and dynamically invokes a method of this instance
var test = new Unchangeable();
var baseType = typeof(Extractor<>).MakeGenericType(test.GetType());
var extractorType = Assembly.GetExecutingAssembly()
.GetTypes().FirstOrDefault(t => t.IsClass && !t.IsAbstract && t.IsSubclassOf(baseType));
if (extractorType != null)
{
dynamic extractor = Activator.CreateInstance(extractorType);
string result = extractor?.ExtractionTwo(test);
}
Of course, it's simplified, you can pass a specific instance of Unchangeable or Untouchable class and restrict assembly types scanning (and get all types only once).
The disadvantage here is that you have to pay attention to ExtractionOne and ExtractionTwo signatures, since they are invoked on dynamic object
The core issue with this approach is that it's unfortunately looking
for a class of the form Extractor<> instead of an interface of the
form IExtractor<>.
This snippet can help you to look through types using IExtrator<> interface
var baseType = typeof(IExtractor<>).MakeGenericType(typeof(Unchangeable));
var extractorType = Assembly.GetExecutingAssembly()
.GetTypes().FirstOrDefault(t => t.IsClass && !t.IsAbstract && baseType.IsAssignableFrom(t));
I think I have a lack of understand, what exactly is happening:
The user can enter a Path to an Assembly and an Object type and I try to create a instance of it.
My approach:
Assembly a = Assembly.LoadFile(txtAssemblyPath.Text);
Type myType = a.GetTypes().ToList().FirstOrDefault(f => f.Name == txtObjectName.Text);
var obj = Activator.CreateInstance<myType>();
var obj2 =(myType) Activator.CreateInstance(myType);
The problem is in the Creation of the Object itself. It seems like the myType is not threated as a Type. On this example: Creating generic variables from a type - How? Or use Activator.CreateInstance() with properties { } instead of parameters ( )?
They just get an object, so I guess this is not the same Case. What I dont understand at all: CreateInstance(Type) works, but CreateInstance with the Type doesn't, but T and Type should be the same: System.Type.
Thanks in advance for clarifying.
Matthias
There are a using difference... When you write:
var obj = Activator.CreateInstance<myType>();
You used your Class like a Type, and it's the good way to do it.
You used a generic type who have a reference to a class type.
But there:
var obj2 =(myType) Activator.CreateInstance(myType);
You used you class like an instance (object). And you can't do that, a class is a schema.
If you want to call the second method, you have to write:
var obj2 =(myType) Activator.CreateInstance(typeof(myType));
This code will create an instance of the class Type, and this instance will describe your class myType.
I hope to be clear.
A class is a schema, you can create an object with this schema, it will be an instance (a memory-object of your class).
When you use generic type of method such as Activator.CreateInstance<T>(); you have to provide Strong Type of T. That means you have to pass known type name instead of T. for example:
var activatorPerson = (Person)Activator.CreateInstance<Person>();
That's why there is a non generic form of Activator.CreateInstance(typeGoesHere) function which can be used in cases that we do not have a Strong Type at moment of creation of an object. So we can pass type as a parameter to that function. we can provide type variable in lots of ways. In your case you find the proper type in your assembly as following:
Type myType = a.GetTypes().ToList().FirstOrDefault(f => f.Name == txtObjectName.Text);
also you have to notice that explicit casting as you typed in your code is not valid:
obj2 =(myType) Activator.CreateInstance(myType);
because you have to provide Strong Type name for explicit casting.
when we do not have access to strong type names at run time, we have to use non generic versions of methods.
Here is the pure dynamic way.
This is the factory class and dynamic instance create method:
public class RepositoryFactory
{
public static dynamic CreateDynamic<TEntity>() where TEntity : BaseEntity
{
dynamic repositoryInstance = null;
var subRepositories = AssemblyHelper.GetSubclassesOf(typeof(BaseRepository<TEntity>), true);
var entityTypeName = typeof(TEntity).Name;
var subRepository = subRepositories.FirstOrDefault(x => x.Name == entityTypeName + "Repository");
if (subRepository != null)
{
var repositoryType = subRepository.UnderlyingSystemType;
repositoryInstance = Activator.CreateInstance(repositoryType);
}
return repositoryInstance;
}
}
This is the helper class for mapping types between Entity and Repository.
public static class AssemblyHelper
{
public static List<Type> GetSubclassesOf(Type type, bool ignoreSystem)
{
List<Type> lReturn = new List<Type>();
foreach (var a in System.Threading.Thread.GetDomain().GetAssemblies())
{
if (ignoreSystem && a.FullName.StartsWith("System."))
{
continue;
}
foreach (var t in a.GetTypes())
{
if (t.IsSubclassOf(type) || (type.IsInterface && t.GetInterfaces().FirstOrDefault(e => e.FullName == type.FullName) != null))
{
lReturn.Add(t);
}
}
}
return lReturn;
}
}
This is the Manager class for use case:
public class PageManager
{
private readonly ContentPageRepository _pageRepository;
public PageManager()
{
_pageRepository = RepositoryFactory.CreateDynamic<ContentPage>();
}
public void GetPagesByContentType(ContentType type)
{
var pages = _pageRepository.GetByPredicate(x => x.Status != EntityStatus.Deleted && x.Node.Type == type);
foreach (var page in pages)
{
//Deal with it :)
}
}
}
I have a base class, SpecialClass. Lots of other classes inherit from it, like WriteSpecialClass and ReadSpecialClass. Instances of these classes are serialized and deserialized, after being casted to/from the base class. Therefore, I have lots of repetitious code like this (for serializing):
SpecialClass sc = null;
if (type.DisplayName == "Read") {
sc = new ReadSpecialClass();
} else if (type.DisplayName == "Write") {
sc = new WriteSpecialClass();
} else
throw new NotSupportedException("Type not supported");
String xml = SerializeToXML(sc);
.. and deserializing:
SpecialClass sc = null;
sc = (SpecialClass)DeserializeFromXML(xml);
switch (someVariableThatDicatesTypeOfSpecialClass) {
case "Read":
sc = (ReadSpecialClass)sc;
break;
case "Write":
sc = (WriteSpecialClass)sc;
break;
}
One more important piece of info: SpecialClass has a bunch of abstract methods defined which each class that inherits from it needs to implement. All the classes which inherit from it conform to the methods, but return different things, so each class is not the same.
I can post my serialization or deserialization methods if needed.
I would like to try and simplify this so that I don't have to specify each SpecialClass-derived class (like ReadSpecialClass). Is there any way to do this? The only way I can think of is duck-typing. Thanks for your help,
Have you considered a serialize() method in SpecialClass? That way if there are some special considerations you can override the base.serialize method and take care of any unique needs. In this way it already knows what it is and the conditional isn't necessary.
Another thing to consider is maybe a helper class with a facade pattern. Then you can have a a method called "public SpecialClass DeserializeSpecialClass()". Then instead of casting the type in the deserializer you could cast it at the target. If you find you are doing too much casting then maybe consider adding abstract methods to the base class that will be realized in the derived class.
Hope this helps.
For serializing, you can use some sort of lookup:
public class SpecialClass
{
private static Dictionary<string, Func<SpecialClass>> factories =
new Dictionary<string, Func<SpecialClass>>();
static SpecialClass()
{
factories["Read"] = () => new ReadSpecialClass();
factories["Write"] = () => new WriteSpecialClass();
}
public static SpecialClass CreateByName(string name)
{
Func<SpecialClass> factory;
if (!factories.TryGetValue(name))
throw new ArgumentException("name", "\"" name +
"\" is not a recognized subclass of SpecialClass.");
return factory();
}
}
For deserialization, these two lines:
sc = (ReadSpecialClass)sc;
sc = (WriteSpecialClass)sc;
perform no actual conversion. The only thing they will do is throw an exception if the object referenced by sc is not of the appropriate type. What you are doing here is roughly the same thing as:
object a = "foo";
a = (string)a;
Sure, the cast will succeed. But it does not in any way modify the object pointed to by a. All it really does is verify that this object is already a string.
if (sc is WriteSpecialClass)
{
sc = (WriteSpecialClass) sc;
}
else if (sc is ReadSpecialClass)
{
sc = (ReadSpecialClass) sc;
}
else
{
throw new NotSupportetException("Type not Supportet");
}
If you're not concerned about performance, you can use reflection to initialize the types whose name contains that type name.
SerializeToXML() takes the base class SpecialClass as a parameter, so it shouldn't distinguish the difference between the derived classes ReadSpecialClass and WriteSpecialClass. Why don't you have SerializeToXML() as an instance method of the base class?
Rule 1./ Your derived classes should be responsible for serializing and deserializing themselves, as they should be the only ones that have intimate knowlege of the additional data that is stored in the XML Doc.
So from a ploymorphic point of view (note your code above is not polymorphic) you would do something like
public class SpecialClass
{
public virtual XElement SerializeToXML()
{
// Base impl
}
}
public class YourDerivedClasses
{
public override XElement SerializeToXML()
{
//derived implementation
}
}
Pretty straight forward. But the problem you have is not one of serialization or polymorphic behaviour, it's one of instantiation of the correct type from the XML.
One way to solve that problem is to have some key in your XML doc that specifies the type that saved it, and register a factory responsible for construction of the derived type indexed by that key (attributes are good for that kind of registration), once the derived type is constructed use it to deserialize the xml.
Note, your factory could also be a static method on the Base class.
Something like, (untested of course)....
public class SpecialClass
{
***snip
public static IEnumerable<SpecialClass> GetAllClasses(XElement xml)
{
IDictionary keyedtypes = GetKeyedTypesDictUsingReflection() // the registered factories
foreach(var x in xml.Elements("YourClassesNode"))
{
string key = //get key from xml
yield return keyedTypes[key].DeserializeFromXML(x);
}
}
}