C
C#4mo ago
stigzler

How to pass a Type to a method for a return of the same type

Struggling to find the right syntax for this. I'm trying to specify a type that should be used to create a new instance and then return it. Example code (doesn't compile):
private T SingleDataObjectFromXDoc<T>(XDocument xDoc, string elementName)
{
XElement SingleElement = xDoc.Descendants(elementName).First();
return new (T)(SingleElement);
}
private T SingleDataObjectFromXDoc<T>(XDocument xDoc, string elementName)
{
XElement SingleElement = xDoc.Descendants(elementName).First();
return new (T)(SingleElement);
}
In this instance, the constructors of each Type I'll be passing will receive the XElement and subsequently update its properties from the XElement. Example calls would be:
object DataObject1 = SingleDataObjectFromXDoc<Server>(xdoc1, "server");
object DataObject2 = SingleDataObjectFromXDoc<User>(xdoc2, "user");
object DataObject1 = SingleDataObjectFromXDoc<Server>(xdoc1, "server");
object DataObject2 = SingleDataObjectFromXDoc<User>(xdoc2, "user");
the return new... is essentially return new Server(XElement) and return new User(XElement) (if that makes sense?) (I know I'm assigning it to an object, but don't worry about that bit!)
29 Replies
TheBoxyBear
TheBoxyBear4mo ago
If you need to create an instance of T, it needs to be listed as a constraint void foo<T>() where T : new() though not the most flexible approach as it assumes the type has an parameterless constructor With the constraints, you can instanciate T like any other type T t = new() You can also use Activator.CreateInstance although that's not type safe With requiring XElement, you can also consider exposing it through a common interface and adding that interface as a constraint to assign the value void foo<T>() where T : ISomeInterface
stigzler
stigzler4mo ago
Hmm. SO I tried:
private T SingleDataObjectFromXDoc<T>(XDocument xDoc, string elementName) where T : new()
{
XElement SingleElement = xDoc.Descendants(elementName).First();
return new T(SingleElement);
}
private T SingleDataObjectFromXDoc<T>(XDocument xDoc, string elementName) where T : new()
{
XElement SingleElement = xDoc.Descendants(elementName).First();
return new T(SingleElement);
}
but getting compile error: "cannot provide arguments when creating an instance of a variable type"
TheBoxyBear
TheBoxyBear4mo ago
Or receiving a construction delegate The new() constraint only works for parameterless consturctors and you can't constraints based on constructors with parameters If you were to refactor your classes, the new() constraint can server as a starting point to create the object from there you can assign the XElement through other means Through a construction delegate: T foo<T>(Func<string, T> ctor) => ctor("arg"); That grants more flexibility over constructors, even supporting static creation methods
stigzler
stigzler4mo ago
All the relevant Types have a public method called UpdateFromXElement - however - tried this - also doesn't compile:
private T SingleDataObjectFromXDoc<T>(XDocument xDoc, string elementName) where T : new()
{
XElement SingleElement = xDoc.Descendants(elementName).First();
var obj = new T();
return obj.UpdateFromXElement(SingleElement);
}
private T SingleDataObjectFromXDoc<T>(XDocument xDoc, string elementName) where T : new()
{
XElement SingleElement = xDoc.Descendants(elementName).First();
var obj = new T();
return obj.UpdateFromXElement(SingleElement);
}
(forgive me - I'm a hobby coder)
TheBoxyBear
TheBoxyBear4mo ago
To use anything with a generic type, it needs to be specified through a constraint. new() is one that allows creating objects, but you also need constraints for the members you access. In your case, an common interface that defines the method you want to use void foo<T>() where T : ISomeInterface
stigzler
stigzler4mo ago
and the interface would have the UpdateFromXElement method specified presumably?
TheBoxyBear
TheBoxyBear4mo ago
Yes, and have each of the clases implement that interface Also I don't know your code but you would likely want to call UpdateFromXElement first then return obj Defining multiple constraints: void foo<T>() where T : new(), InterfaceA, InterfaceB
stigzler
stigzler4mo ago
Hmmm
stigzler
stigzler4mo ago
No description
stigzler
stigzler4mo ago
No description
TheBoxyBear
TheBoxyBear4mo ago
What is the error on new()? I remember there's a specific order, maybe I had it the wrong way around
stigzler
stigzler4mo ago
No description
TheBoxyBear
TheBoxyBear4mo ago
^ my bad
stigzler
stigzler4mo ago
ok Other comile error is:
stigzler
stigzler4mo ago
No description
TheBoxyBear
TheBoxyBear4mo ago
Because the method is void and you're returning from the call Should be distinct, calling it then returning obj
stigzler
stigzler4mo ago
ohbloody hell yes! Let me take it for a spin
TheBoxyBear
TheBoxyBear4mo ago
:spinning_think:
stigzler
stigzler4mo ago
Perfect!
private T SingleDataObjectFromXDoc<T>(XDocument xDoc, string elementName) where T : IScreenscraperEntity, new()
{
XElement SingleElement = xDoc.Descendants(elementName).First();
var obj = new T();
obj.UpdateFromXElement(SingleElement);
return obj;
}
private T SingleDataObjectFromXDoc<T>(XDocument xDoc, string elementName) where T : IScreenscraperEntity, new()
{
XElement SingleElement = xDoc.Descendants(elementName).First();
var obj = new T();
obj.UpdateFromXElement(SingleElement);
return obj;
}
public interface IScreenscraperEntity
{
void UpdateFromXElement(XElement xElement);
}
public interface IScreenscraperEntity
{
void UpdateFromXElement(XElement xElement);
}
TheBoxyBear
TheBoxyBear4mo ago
:HYPERSclap:
stigzler
stigzler4mo ago
apiReturn.DataObject = SingleDataObjectFromXDoc<Server>(xDoc, "serveurs");
apiReturn.DataObject = SingleDataObjectFromXDoc<Server>(xDoc, "serveurs");
Cheers dude! Now I got some serious reading to do about "constraints" - new one to me
TheBoxyBear
TheBoxyBear4mo ago
tl;dr The generic system is based on better safe than sorry, so assumes the worst that T is System.Object unless constrainted to something more specific
stigzler
stigzler4mo ago
and of course, generic Object has no custom Methods, so the constraint tells the method what you can and can't do with T? (e.g. new it up or use a method defined via an interface?) - layman's terms hah - so it "constrains T" from generic to some kind of ball park - awesome! Thanks for all your help. 👍
TheBoxyBear
TheBoxyBear4mo ago
Just watch out when getting nullability involved shivers from flashbacks - varying actual types depending if T is struct or class
stigzler
stigzler4mo ago
not easy to debug?
TheBoxyBear
TheBoxyBear4mo ago
More of a beginner's trap
stigzler
stigzler4mo ago
then I'll definitely end up in it 😉 Damn - this is quite powerful - given it's an interface the common method can be different in each class 🤯
TheBoxyBear
TheBoxyBear4mo ago
In recent .NET, static interface members are also suppoted For anything numbers, there's the new System.Numerics as a backbone of every numeric type
stigzler
stigzler4mo ago
tbh, I still feel like I need to learn to toddle before I try to break into a sprint!