Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Return interface inherited classes in Generic method

I have 3 objects that are very similar with only a few differences

public class Person
{
    public Person(ResourceObject resource)
    {
        // resource comes from an API provided by one
        // of our systems (i have no control over it)
        this.ResourceObject = resource;
    }

    // Resource
    internal ResourceObject ResourceObject { get; }

    // Similar properties
    public string ObjectID { get; }
    public string ObjectType { get; }
    public IEnumerable<string> PropertyNames { get; }

    // Person-specific property example - Organisation
    public string Organisation { get; set; }
}

public class Computer
{
    public Computer(ResourceObject resource)
    {
        // resource comes from an API provided by one
        // of our systems (i have no control over it)
        this.ResourceObject = resource;
    }

    // Resource
    internal ResourceObject ResourceObject { get; }

    // Similar properties
    public string ObjectID { get; }
    public string ObjectType { get; }
    public IEnumerable<string> PropertyNames { get; }

    // Computer-specific property example - OperatingSystem
    public string OperatingSystem { get; set; }
}

public class Group
{
    public Group(ResourceObject resource)
    {
        // resource comes from an API provided by one
        // of our systems (i have no control over it)
        this.ResourceObject = resource;
    }

    // Resource
    internal ResourceObject ResourceObject { get; }

    // Similar properties
    public string ObjectID { get; }
    public string ObjectType { get; }
    public IEnumerable<string> PropertyNames { get; }

    // Group-specific property example - Members
    public string Members { get; set; }
}

I currently have GetPerson, GetComputer and GetGroup methods that are working but they essentially do the same thing and then call one of the specific object constructors. In an effort to dive into the world of Generics and Interfaces and learn more (as you do) i attempted to create a GetResource<T> method that would do the same job as those 3 methods without all the duplicate code.

I created the IResource Interface to identify common properties:

public interface IResource
{
    string ObjectID { get; }
    string ObjectType { get; }
    IEnumerable<string> PropertyNames { get; }
}

and then attempted to create a GetResource<T> method but got stuck at the return code:

    public static T GetResource<T>(string identity) where T : IResource
    {
        // get resource from system API

        // and then return T somehow?
        return new T(resourceObject);
    }

I thought of changing the return value from T to IResource but i'm still not sure how i would identify which class to return (Perhaps i need a base class? Resource perhaps).

The reason i turned to Generics for this specific situation is if the system API updates and suddenly has a new Location object i don't want to have to create a GetLocation method and then have 4 methods that do exactly the same thing except for one line of code.

Is this the correct use case for Generics? and if so how can my method figure out what object to return?

like image 242
Bluecakes Avatar asked Jun 22 '26 11:06

Bluecakes


1 Answers

Use a base class to hold common behavior.

public abstract class Resource {

    protected Resource (ResourceObject resource) {
        // resource comes from an API provided by one
        // of our systems (i have no control over it)
        this.ResourceObject = resource;
    }

    // Resource
    internal ResourceObject ResourceObject { get; }

    // Similar properties
    public string ObjectID { get; }
    public string ObjectType { get; }
    public IEnumerable<string> PropertyNames { get; }
}

Derived classes

public class Person : Resource {

    public Person(ResourceObject resource):base(resource){
    }

    // Person-specific property example - Organisation
    public string Organisation { get; set; }
}

public class Computer : Resource {
    public Computer(ResourceObject resource) : base(resource) {
    }

    // Computer-specific property example - OperatingSystem
    public string OperatingSystem { get; set; }
}

public class Group : Resource {

    public Group(ResourceObject resource) : base(resource) {
    }

    // Group-specific property example - Members
    public string Members { get; set; }
}

Interfaces can't be initialized and thus trying to pass a constructor argument wont work.

With the base class constraint the generic method becomes

public static T GetResource<T>(string identity) where T : Resource {
    // get resource from system API

    // and then return T somehow?
    return (T) Activator.CreateInstance(typeof(T), resourceObject);
}

And used

Person person = GetResource<Person>("person_identity");
like image 113
Nkosi Avatar answered Jun 25 '26 01:06

Nkosi



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!