Home > OS >  Create an instance of a derivate class adding all data
Create an instance of a derivate class adding all data

Time:10-08

I'm creating component to generate form at run-time with Blazor and C# (NET6). I have an issue with list of interfaces. Let me explain.

I have an interface for all element called IElement

public interface IElement
{
    public string? Type { get; set; }
    public string? Name { get; set; }
}

Then, I have few classes that inherit from it, for example

public class Textbox : IElement
{
    [JsonPropertyName("type")]
    public virtual string? Type { get; set; }
    [JsonPropertyName("name")]
    public string? Name { get; set; }

    [JsonPropertyName("text")]
    public string? Text { get; set; }
}

public class Radiobutton : IElement
{
    [JsonPropertyName("type")]
    public virtual string? Type { get; set; }
    [JsonPropertyName("name")]
    public string? Name { get; set; }

    [JsonPropertyName("choises")]
    public List<string> Choises = new List<string>();
}

Now, I'm creating a component when I want to repeat multiple times the some 2 element (for example). So, I create a

List<IElement>? RepeaterElements { get; set; }

In this list I add 2 elements: one Textbox and one Radiobutton with some properties. I created a function to create at run-time an instance of the derived class.

void AddRow()
{
    ElementData.NumberOfRows  = 1;
    row  ;

    int question = 0;
    foreach (var el in ElementData.RepeaterElements)
    {
        question  ;

        var newInstance = el.GetType();
        var instance = Activator.CreateInstance(newInstance) as IElement;
        instance.Parent = ElementData.Name;
        instance.Index = row;
        instance.QuestionNumber = question.ToString();
        instance.Name = instance.GetElementName();

        RepeaterElements.Add(new RepeaterElement() { Element = instance, Row = row });
    }
}

How can I have in the new instance all the property values?

I tried to add an object like

var instance = Activator.CreateInstance(newInstance, new object[] { el }) as IElement;

but it is not working because I get the following exception

crit: Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100] Unhandled exception rendering component: Constructor on type 'PSC.Survey.Shared.Checkbox' not found. System.MissingMethodException: Constructor on type 'PSC.Survey.Shared.Checkbox' not found. at System.RuntimeType.CreateInstanceImpl(BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture) at System.Activator.CreateInstance(Type type, BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture, Object[] activationAttributes) at System.Activator.CreateInstance(Type type, Object[] args) at PSC.Blazor.Components.Survey.Components.Repeater.AddRow() in C:\Projects\FromEnricoRossiniDevOps\SurveyGenerator\PSC.Blazor.Components.Survey\Components\Repeater.razor.cs:line 55 at Microsoft.AspNetCore.Components.EventCallbackWorkItem.InvokeAsync[Object](MulticastDelegate delegate, Object arg) at Microsoft.AspNetCore.Components.EventCallbackWorkItem.InvokeAsync(Object arg) at Microsoft.AspNetCore.Components.ComponentBase.Microsoft.AspNetCore.Components.IHandleEvent.HandleEventAsync(EventCallbackWorkItem callback, Object arg) at Microsoft.AspNetCore.Components.EventCallback.InvokeAsync(Object arg) at Microsoft.AspNetCore.Components.RenderTree.Renderer.DispatchEventAsync(UInt64 eventHandlerId, EventFieldInfo fieldInfo, EventArgs eventArgs)

CodePudding user response:

Because the default constructor of the Checkbox is replaced by other constructors. Based on the error you receive and the code your provided, I guess you are missing the default constructor (the empty one) in your Checkbox class. To fix that, simply add one as below:

public class Checkbox: IElement
{
   // Add this default Constructor
   public Checkbox() {}
   
   // Other existed constructor 
   // public Checkbox(Type1 arg1, Type2 arg2) {}
}

The reason why the Textbox and Radiobutton don't throw errors because you don't specify any custom constructor for them, so the default constructor would be used.

If you don’t provide a constructor for your class, C# creates one by default that instantiates the object and sets member variables to the default values. A constructor without any parameters is called a default constructor

In your case, based on the documents of System.Activator.CreatetInstance - CreateInstance(Type, BindingFlags, Binder, Object[], CultureInfo, Object[]). You are not passing any paremeter to instantite the Element, so it uses the Default Constructor

CodePudding user response:

The AddRow method doesn´t make sense to me. You seem to iterate RepeaterElements but inside the foreach statement you add new elements to the list.

Anyway, when working with interfaces you can have a list of different types (but all of them implementing the interface) like you did. And you can cast the elements of the list to the type you need to get all the properties and concrete methods. I Believe that the propety Type in IElement is just for this. Maybe something like:

if(el.Type == "Textbox") { var castedEl = el as Textbox; } // here you access all properties and methods of Textbox in te castedEl object.

Another thing you can use is the "is" operator:

if(el is Textbox) { ((Textbox)el).Text = ""; }

  • Related