I am having a strange error in one of my solutions while attempting to use Activator.CreateInstance having changed the parameter for the .ctor on the type being created from a plain generic T to an IEnumerable. I have extracted enough code to a console app to test in isolation but it appears to work just fine.
Below is the extracted code that works in the console app -
class Program
{
static void Main(string[] args)
{
Notify(new List<MyBase> { new MyBase(), new MyBase() });
}
private static void Notify<T>(IEnumerable<T> changes) where T : IMy
{
var dtoType = changes.First().GetType();
var type = typeof(MyNotification<>).MakeGenericType(dtoType);
var notification = (IMyNotification)Activator.CreateInstance(type, new object[] { changes });
}
}
public interface IMy { }
public class MyBase : IMy { }
public interface IMyNotification { }
public interface IMyNotification<T> : IMyNotification where T : IMy
{
}
public class MyNotification<T> : IMyNotification<T> where T : IMy
{
public MyNotification(IEnumerable<T> mys) { }
}
Essentially this is the same code as is running in my original solution.
The error is a MissingMethodException, so it cannot find a matching .ctor.
Run out of ideas on what could be causing this, looking at the type information in the debugger for both solutions I cannot see any difference. All projects are cleaned and built with the solution.
edit
Hoping someone can point me in another direction to potentially solve this issue.
Thanks
edit
I have tried changing the .ctor to be of type 'object' and with that change Activator can create the type.
Just use new MyNotification<T>(changes) if it is what you need.
Related
I have a specialized generic collection class which will be used to hold collections of many different types of objects. Once the collection is created, I need to instantiate the collection's items. I am having the darnedest time getting this to work. There must be a simple solution I am missing.
Here is a sample class which kind of illustrates what I am trying to do and the warnings/errors I am bumping up against.
// Note: T may either a string or other reference type that supports IEnumerable.
public class Foo<T>
{
private List<T> fooBarList = new List<T>();
public Foo()
{
Bar1<T>();
Bar2<T>();
Bar3<T>();
}
public void Bar1<T>()
{
// Error Message: Argument 1 cannot convert from 'T...' to 'T...'
T t = default;
fooBarList.Add(t);
}
public void Bar2<T>() where T : IEnumerable, new()
{
// Error Message: T must be a non-abstract type with public
// parameterless constructor in order to use it as a parameter 'T'
// in the generic type or method 'Foo<T>.Bar2<T>()
fooBarList.Add(new T());
}
public void Bar3<T>() where T : IEnumerable, new()
{
// Error Message: Argument 1 cannot convert from 'T...' to 'T...'
T t = Activator.CreateInstance<T>();
fooBarList.Add(t);
}
}
Side note: This particular code is in a particularly performance-critical part of my application--you know, the 3% Donald Knuth talks about needing to actually be optimized. This really does need to be fast because it will get called millions of times per application execution. I would not be at all enthusiastic about using reflection (e.g. Activator.CreateInstance() here) if there is any other alternative. (For now, even that does not seem to be working for me.) I would much rather have the compiler resolve the data type at compile time.
This question was already answered in the link below, but none of the approaches seem to be working for me. What am I missing?
In C#, how to instantiate a passed generic type inside a method?
FYI, I am using .NET Core 2.2 Beta and .NET Standard 2.0 on a Windows 10 machine running Visual Studio 2019 Enterprise Preview.
It seems like List<T> already has all you need except a method to create a new instance and add it, which could be added as extension methods:
public static ICollectionExtensions
{
public static AddNew<T>(this ICollection<T> collection)
where T : new()
{
var newItem = new T();
collection.Add(newItem);
}
...
}
which can be used like this:
var list = new List<int>();
list.AddNew();
This compiles:
public class Foo<T> where T : IEnumerable, new()
{
private List<T> fooBarList = new List<T>();
public Foo()
{
Bar1();
Bar2();
Bar3();
}
public void Bar1()
{
T t = default(T);
fooBarList.Add(t);
}
public void Bar2()
{
fooBarList.Add(new T());
}
public void Bar3()
{
T t = Activator.CreateInstance<T>();
fooBarList.Add(t);
}
}
Note that the only declaration of T is up at the class level, both the <T> part and the where part.
I am facing with a problem when loading dynamically a library with a shared reference. I understood where the problem could be, but I don't really know how I can fix it.
I will try to explain my problem better, this is my structure:
I have 3 assemblies
DataProviderAssembly
ContractsAppAssembly
AppAssembly
I start saying that I can not currently use MEF.
The "DataProviderAssembly" loads dinamically the "AppAssembly" because it has a type that implements the IApp interface from the "ContractsAssembly".
Following the code below, when I try to call the method OnSignRequest from the App class, I get a MissingMethodException. Seems that the method were not found, but, if I remove the "RestRequest" parameter, everything works fine.
I think that, the dynamic loading of that DLL miss some reference information for the dependent assemblies.
**ContractsAppAssembly**
public interface IApp {
void GetData();
}
public abstract class MainClassBase : IApp {
public virtual void GetData(){
//Do Stuff
}
protected void OnSignRequest(RestRequest request){
//calling sign request event
}
}
public abstract class SecondClassBase : MainClassBase {
// i need this because it overrides other methods i didn't specify here
}
**AppAssembly**
public class App : SecondClassBase {
public override void GetData(){
RestRequest request = new RestRequest();
base.OnSignRequest(request);//<-- this throws a MissingMethodException
}
}
**DataProviderAssembly**
public class DataProvider{
public DataProvider{
string[] appsLibs = Directory.GetFiles(appsFolderPath, "*.dll", SearchOption.TopDirectoryOnly);
foreach (string appLib in appsLibs)
{
Assembly appAssemlby = Assembly.LoadFrom(appLib);
Type appType = providerAssemlby.GetTypes().FirstOrDefault(t => t.GetInterface(typeof(IApp).Name, true) != null && !t.IsAbstract);
if (appType == default(Type))
{
continue;
}
try
{
IApp app = (IApp )Activator.CreateInstance(appType);
initApp(app);
this.apps.Add(app.Name, app);
}catch(Exception ex)
{
}
}
}
}
All the assemblies reference RestSharp.dll, the build structure is the following:
Debug:
DataProviderAssembly.dll
Debug/Apps:
ContractsAppAssembly.dll
AppAssembly.dll
Obviously the DataProviderAssembly.dll has the "probing" option inside of the app.config file.
Debugging this code I cannot reach the "base.OnSignRequest" line of code inside of the App class , but, if I declare virtual the method "OnSignRequest" inside of the MainClassBase, the debugger let me reach that line of code giving me the exception only when I try to do a step on.
I have some code base which has is calling the following:
SetHazardDataService();
namespace Analytics.Foo.DataServices
{
class HDB:IDataService
{
}
}
With a member function declared in another class/file
using Analytics.Foo.DataServices
public void MyDataService()
{
var DbDataSvc = new HDB();
}
originally, I see the same definition used elsewhere but with (no idea if that works):
protected void MyDataService()
I included the public method in my class
I'm now trying to recreate that functionality, but I get the following issue:
The type Analytics.Foo.DataServices.HDB' has no constructors defined
I'm not sure what the issue is - any suggestions for why this is the case. There is no constructor that I can see. Plus I'm not able to see the other code working/but it doesn't give the same issue.
You need to create a constructor to class HDB, like this:
namespace Analytics.Foo.DataServices
{
class HDB:IDataService
{
public HDB()
{
}
}
}
Beautiful sunny day today! However, I can't enjoy it because I've been trying to call a dynamic method in Mono for 2 days :-(
The Story:
I'm trying to call it within a class called 'Template'. Basically I would love it if I could pass a string to Template and have it run that method, which is defined within the Template class. The template class looks like this so far..
namespace Mash
{
public class Template
{
public Template(string methodToCall)
{
Type type = this.GetType();
object ob = Activator.CreateInstance(type);
object[] arguments = new object[52];
type.InvokeMember(methodToCall,
BindingFlags.InvokeMethod,
null,
ob,
arguments);
}
public void methodIWantToCall()
{
Console.WriteLine("I'm running the Method!");
}
}
}
No errors are received during compile time. Once I run it, however, I get
'Unhandled Exception: System.MissingMethodException: Method not found: 'Default constructor not found...ctor() of Mash.Template'.'
I think it is failing here:
object ob = Activator.CreateInstance(type);
If you need any more information please let me know.
Thanks in advance!!
you don't need another instance of Template if the method you want to call is in the same class.You can use this
public class Template
{
public Template(string methodToCall)
{
this.GetType().InvokeMember(methodToCall,
BindingFlags.InvokeMethod,
null,
this,
null);
}
public void methodIWantToCall()
{
Console.WriteLine("I'm running the Method!");
}
}
I tested it with:
class Program
{
public static void Main(string[] args)
{
Template m = new Template("methodIWantToCall");
Console.ReadKey(true);
}
}
The first argument of Activator.CreateInstance is the type of the class, and then follows the argument of the constructor of the type.
You're trying to create an instance of the Template class using no parameter for the constructor. But there isn't a constructor with no parameter.
Try adding a constructor into your Template class, which takes no parameters:
public class Template
{
//......
public Template()
{
}
}
I just need another pair of eyes... I don't see anything wrong with the following. In fact, I swear I had something just like this not long ago, and it worked.
In my Collections.dll:
namespace Collections
{
public class CSuperAutoPool
{
public static CSuperAutoPool ActivateByType(Type typeToBeActivated, params object[] activatedArguments)
{
//...
}
}
}
In another DLL, I have referenced the collections DLL project, and use it in this function:
namespace Organization
{
public class CBaseEntity : CSuperAutoPool
{
protected static CBaseEntity Create()
{
//...
CBaseEntity created = (CBaseEntity)CSuperAutoPool.ActivateByType(callingType); //Error here.
//...
}
}
}
Error: 'Collections.CSuperAutoPool' does not contain a definition for 'ActivateByType'
I have used ActivateByType, within CSuperAutoPool, in a different function, and that one does not have errors. The Collections DLL compiles without errors. In the same DLL where the Organization namespace exists, have used various other aspects of the CSuperAutoPool class in other ways, without compiler errors.
There must be something missing from your example, or you are not using the version of the code that you think you are using, e.g. could it be that there is another class called CSuperAutoPool in your project, possibly in a referenced assembly?
The following snippets compiles without errors:
namespace Collections
{
public class CSuperAutoPool
{
public static CSuperAutoPool ActivateByType(
Type typeToBeActivated, params object[] activatedArguments)
{
//...
return null;
}
}
}
namespace Organization
{
using Collections;
public class CBaseEntity : CSuperAutoPool
{
protected static CBaseEntity Create()
{
Type callingType = null;
//...
CBaseEntity created =
(CBaseEntity)CSuperAutoPool.ActivateByType(callingType);
//...
return created;
}
}
}
Found it! 0xA3 gave me the hint I needed with: "you are not using the version of the code that you think you are using"
When I added the Collections reference to the Organization project, it did not checkmark the Collections project to compile in the Configurations Manager. In other words, my Collections DLL was not compiling unless I did it by hand.
Thank you, that's what I meant by an extra set of eyes. :-)