Get an interface property value in a generic class - c#

I have this interface
public interface IMyInterface
{
IEnumerable<MyParamInfo> Params { get; }
}
where
MyParamInfo is
public class MyParamInfo
{
public MyParamInfo (string name)
{
Name= name;
}
public string Name { get; private set; }
}
also
this class
public class MyClass:IMyInterface
{
//properties
....
public IEnumerable<MyParamInfo> Params
{
get
{
return new List<MyParamInfo> { new MyParamInfo("Param1")};
}
}
}
and this Form
public partial class MyForm<T> : Form where T:Class,IMyInterface
{
...
}
with this code
MyForm<MyClass> frm = new MyForm<MyClass>();
How can I access to Params Property of MyClass in frm object?

If you also require that the T type parameter of MyForm have a parameterless constructor, you can instantiate an instance of T and then use the interface property at will.
On the definition of MyForm, add the new() generic constraint
public partial class MyForm<T> : Form where T : Class, IMyInterface, new()
Then in some method of MyForm<T>, you can use:
(new T()).Params;
You can read about all the constraints on type parameters in C# here.
It seems like what you really want is interfaces that could specify static methods (so-called static interfaces). Such a construct does not exist in C#.

Related

'Type' cannot satisfy the 'new()' constraint on parameter 'TParam' because 'Type' has required members

I have this class structure (simplified):
public class InducingMedium
{
public required string File { get; set; }
}
public class InducingVideo : InducingMedium {}
public class InducingAudio : InducingMedium {}
Now, I want to generically instantiate an instance of a specific type:
public abstract class BaseInducingTests<TMedium>
where TMedium : InducingMedium, new()
{
protected async Task<IEnumerable<TMedium>> CreateInducingMedia(IEnumerable<string> files)
{
return files.Select(file =>
{
// Do some processing...
return new TMedium
{
File = file,
};
});
}
}
public class InducingVideosTests : BaseInducingTests<InducingVideo>
{
}
But in the derived class I get an error:
'Namespace.InducingVideo' cannot satisfy the 'new()' constraint
on parameter 'TMedium' in the generic class 'Namespace.Tests.BaseInducingTests<TMedium>'
because 'Namespace.InducingVideo' has required members
Is there any way to fix this without introducing reflection?
I was really excited about required members, which work pretty well with nullable types, but now I see this has its own caveats :(
This is explicitly mentioned in the docs:
A type with any required members may not be used as a type argument when the type parameter includes the new() constraint. The compiler can't enforce that all required members are initialized in the generic code.
Either remove the required modifier or change the generic handling. For example provide factory via ctor:
public abstract class BaseInducingTests<TMedium>
where TMedium : InducingMedium
{
private readonly Func<string, TMedium> _init;
public BaseInducingTests(Func<string, TMedium> init)
{
_init = init;
}
protected async Task<IEnumerable<TMedium>> CreateInducingMedia(IEnumerable<string> files)
{
return files.Select(file => _init(file));
}
}
public class InducingVideosTests : BaseInducingTests<InducingVideo>
{
public InducingVideosTests() : base(s => new InducingVideo{File = s})
{
}
}
If needed you can then create a wrapper to support classes which satisfy new() constraint:
public abstract class BaseNewableInducingTests<TMedium> : BaseInducingTests<TMedium>
where TMedium : InducingMedium, new()
{
protected BaseNewableInducingTests() : base(s => new TMedium { File = s })
{
}
}
Another workaround which I've just found is SetsRequiredMembers attribute:
public class InducingVideosTests : BaseInducingTests<TestInducingVideo>
{
}
public class TestInducingVideo : InducingVideo
{
[SetsRequiredMembers]
public TestInducingVideo()
{
}
}
This kind of defeats the purpose of required members, but so does reflection and for test purposes it's an easier way.
Source: https://code-maze.com/csharp-required-members/

How to access properties of generic base class's parameter in C#?

I have a common class PopupDialog which has properties such as bool IsBackDismissEnabled. This is used to denote whether pressing back button will dismiss the dialog.
I am using Xamarin.CommunityToolkit Popup for showing a popup dialog.
Here's my PopupDialog.cs
public class PopupDialog
{
public bool IsBackDismissEnabled { get; set; }
}
Here's my Dialog Implementation
using Xamarin.CommunityToolkit.UI.Views;
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class ForgotPasswordDialog : Popup<PopupDialog>
{
public ForgotPasswordDialog()
{
InitializeComponent();
// I want to access IsBackDismissEnabled here
// something like base.IsBackDismissEnabled = true;
}
}
I want to access PopupDialog's IsBackDismissEnabled from the derived class of Popup<PopupDialog> how can it be done?
In short, I have a class which is specified as a parameter to a generic class. And that generic class is derived in a class from which I want to access properties of previous class that is specified as a parameter to the generic class.
I've never seen anyone attempt to do what you are attempting. I'm not even sure it makes logical sense. Please explain what you are trying to do: Why do you want PopupDialog to be a generic parameter?
Consider making it a parameter on Popup's constructor, and save it in a property or field:
public class Popup
{
public Popup(ISomeInterface myParam)
{
this.MyParam = myParam;
}
public ISomeInterface MyParam;
}
public class ForgotPasswordDialog : Popup
{
public ForgotPasswordDialog() : base(new PopupDialog())
{
}
void SomeMethod()
{
// Access MyParam
... this.MyParam.IsBackDismissEnabled ...
}
}
public interface ISomeInterface
{
bool IsBackDismissEnabled { get; set; }
}
public class PopupDialog : ISomeInterface
{
public bool IsBackDismissEnabled { get; set; }
...
}
/// Usage
var myVariable = new ForgotPasswordDialog();
... myVariable.MyParam.IsBackDismissEnabled ...
To be useful, you'll want to specify an interface or base class that MyParam has. Here I show ISomeInterface. This might instead be some base class of PopupDialog.

Create Instance of type using base class with generics

I've created a plugin system within my code, which loads types from DLLS. I grab the type I want from the loaded DLL using this code;
var type = Assembly.LoadFrom(filePath).GetTypes()
.FirstOrDefault(t =>
t.IsClass && t.IsSubclassOfRawGeneric(typeof(DespatchBasePlugin<>)));
IsSubClassOfRawGeneric hunts down the base type as it is buried several classes down, the code works and the correct type is returned.
I then create an instance of this class using Activator;
DespatchBasePlugin<XMLSettingBase> obj = Activator.CreateInstance(type, new object[] { logger }) as DespatchBasePlugin<XMLSettingBase>;
Unfortunately the cast on this line creates a null reference. Removing the cast returns an instance of the class in question, but I need to store is as its base type.
This is the class being loaded(Shortened for brevity);
public class DHLPlugin : DespatchBasePlugin<UserSetting>
{
public DHLPlugin(BaseForm logger) : base("DHL", logger)
{
this.order = 10;
}
}
And this is the base class I want it to use(Note the class itself has a base class, it goes several layers deep);
public abstract class DespatchBasePlugin<TSettings> : DespatchBase<TSettings> where TSettings : XMLSettingBase, new()
The previous code used a base class with no generic assigned to it and worked absolutely fine. It looked like this;
DespatchBasePlugin obj = Activator.CreateInstance(type, new object[] { logger }) as DespatchBasePlugin;
I'm sure I'm doing something dumb, please tell me what it is.
Edit - Not marked this as duplicate as I believe this is a better question/answer than the other which consists of a generic link to MSDN as the answer. If this is not a correct way to use the duplicate system please let me know.
You can use contravariance to define your plugin:
public class Program
{
public static void Main()
{
var settings = new DerivedSettings()
{Name = "John"};
DerivedPlugin a = new DerivedPlugin(settings);
IPlugin<BaseSettings> sample = (IPlugin<BaseSettings>)a;
Console.WriteLine(sample.GetName());
}
}
public abstract class BaseSettings
{
public abstract string Name
{
get;
set;
}
}
public interface IPlugin<out TSettings>
where TSettings : BaseSettings
{
string GetName();
}
public abstract class BasePlugin<TSettings> : IPlugin<TSettings> where TSettings : BaseSettings
{
protected readonly TSettings _settings;
public BasePlugin(TSettings settings)
{
_settings = settings;
}
public virtual string GetName()
{
return _settings.Name;
}
}
public class DerivedSettings : BaseSettings
{
public override string Name
{
get;
set;
}
}
public class DerivedPlugin : BasePlugin<DerivedSettings>
{
public DerivedPlugin(DerivedSettings settings): base (settings)
{
}
}
I've included a BasePlugin class, but this is optional and you can just directly use the interface.

Define which type will be used in a constructor

I would like to associate an object of type AbstractObject to my current Class.
However, this has to be done in the constructor, since, when I define my Class I don't know which type of object would be associated (only that this is of type AbstractObject). And I need to construct the associated object in my class (So I can't put an instance as parameter).
So it would be something like:
public abstract class MyClass
{
public MyClass(Type T) where T : AbstractObject
{
(T)Actiocator.CreateInstance(Typeof(T));
//To do
}
}
but this doesn't work. Any idea how to fix this?
Depending on your use case, there are several options.
Passing in the object
The simplest way is have the caller construct the object, and pass it in through the constructor:
public MyClass(AbstractObject template)
{
// Do something with template
}
Provide a static factory method
Expanding on the idea above, if you want to have control over the object that the constructor uses, you can provide a static method that creates a Base object and passes it to the constructor:
private MyClass(AbstractObject template)
{
// Do something with template
}
public MyClass Create<T>() where T : AbstractObject, new()
{
// Create a temporary object just for passing into the private ctor
return new MyClass(new T());
}
I made the constructor private so you can create a new MyClass object only through the static instance:
MyClass.Create<Concrete>();
Also note I added the new() constraint, so I can simply write new T. This is fine if you know that T is going to a be a derived class of AbstractObject which is a reference type. If you want to be able to construct MyClasses from value types such as int, you can drop the new() constraint and use reflection.
Make the class generic
If you also need to store the object in your class, make the whole class generic:
public class MyClass<T> where T : AbstractObject, new()
{
private T myObject;
public MyClass()
{
this.myObject = new T();
// Do other stuff
}
}
Create a interface or a abstract class them implement it in the class you want to pass into the current class constructor
public interface ITest
{
// your interface method and properties
}
public class Child : ITest
{
// do your stuff here
}
public abstract class MyClass
{
public MyClass(ITest tes)
{
// do stuff using test
}
}
maybe something like this would suit you?
abstract class AbstractObject {}
class Test : AbstractObject
{
public Test()
{
Console.WriteLine("I work");
}
}
class GenTest<T> where T: AbstractObject, new()
{
T obj;
public GenTest()
{
obj = new T();
}
}
public static void Main()
{
var genTestObj = new GenTest<Test>();
}
Try this:
public abstract class MyClass<T> where T : AbstractObject, new()
{
public MyClass(T type)
{
T instance = new T();
}
}
If you add new() in the generics constraints, you can call the parameterless constructor of your class.
https://msdn.microsoft.com/en-us/library/bb384067.aspx

Is there a way to assign generic through instantiation?

Is there a way to assign T through instantiation as in the following "hypothetical" example:
MyObject DummyObject = new MyObject();
DummyObject.MyNestedObject = new MySecondObject<string>()
public class MyObject //**Normally is specified as MyObject<T>**
{
public MySecondObject<T> MyNestedObject { get; set; }
}
I want to define T on instantiation of MySecondObject. I dont want to define it in MyObject.
No, you have to declare T in your class. Otherwise T won't exist in that context.
public class MyObject<T>
Another way would be to create an interface, implement it in your MySecondObject generic class.Then make the property type to interface instead of MySecondObject<T>
interface IMyInterface
{
}
class MySecondObject<T> : IMyInterface
{
}
public class MyObject
{
public IMyInterface MyNestedObject { get; set; }
}

Categories