简体   繁体   中英

How to implement a dynamic generic type return method without casting at runtime in C#?

Say I want to convert the below implementation in Java to C#, so that I don't have to cast the returned value at runtime, where the casting should already be handled in the get method.

why not create setters and getters, if you ask? simply because I plan to have 50-100+ attributes and I don't want to create setters and getters for every attributes.

[c#] - what i want to end up doing in c#

string name = playerA.getAttribute(Attribute.NAME);
int age = playerA.getAttribute(Attribute.AGE);

Currently I can't unless I cast the returned value to the correct type. But can I do that casting in side the get method before returning?

[Java] - anyhow, this is the current java implementation that works without casting

//setting attributes
playerA.setAttribute(Attribute.NAME, "Tom");
entityB.setAttribute(Attribute.AGE, 4);
...
//getting the attribute without casting
string name = playerA.getAttribute(PlayerAttribute.NAME);
int age = playerB.getAttribute(PlayerAttribute.AGE);

The method inside a Player/Entity is setup like this to get attributes
[Java]

public <E> E getAttribute(Attribute attr){
    //atrributeRepository is EnumMap<Attribute, Object>
    //how I will get my attribute value type at runtime
    Object result = attributeRepositoryMap.get(attr);

    //the enumMap will only ever hold these three type for this example
    if(result instanceof Boolean){ return (E) (Boolean) result; }
    if(result instanceof String){ return (E) (String) result; }
    if(result instanceof Integer){ return (E) (Integer) result; }

    return null;    
    //say all checks are in place and null will never be reach
}

The closest I was able to get in c# is this.

[c#] - though I can deal with it, i would like to prevent casting

string name = (string) getAttribute<string>(Attribute.NAME);
int age = (int) getAttribute<int>(Attribute.AGE);

the method

public T getAttribute<T>(Attribute attribute){
{
     Object result = attributeRepositoryDictionary[attribute];
     return (T)result;
}

is this as closest as I can get with c#, where casting is needed for getting attributes?

I'm not sure I really like it as an idea - but you could do this by making Attribute generic:

public static class Attributes
{
    public static Attribute<int> Age = new Attribute<int>("age");
    public static Attribute<string> Name = new Attribute<string>("name");
}

public class Attribute<T>
{
    public string Key { get; }

    public Attribute(string key)
    {
        Key = key;
    }

    ...
}

Then you can implement your method as:

public T GetAttribute<T>(Attribute<T> attribute)
{
    // attributeDictionary would be a Dictionary<string, object>
    return (T) attributeDictionary[attribute.Key];
}

At that point, type inference will be your friend, so you can just write:

int a = player1.GetAttribute(Attributes.Age);

and it'll be equivalent to:

int a = player1.GetAttribute<int>(Attributes.Age);

I was able to find an alternative solution along with @Jon Skeet, which this was more of what I was looking for (not sure if it is an ideal design though). Anyhow, little did I know there was a keyword called "dynamic" in cSharp.

It allowed my method getAttribute() to return any type at runtime, The only requirement is that you must know the return type.

So I do not recommend using this approach if you plan to work with many return types that are in the 5+. That's why I would recommend using something like an enum to give clues on what type will be returned.

In my case, I will only deal with the basic common return types (int, string, float, bool), so its rather easy to know what type will be returned base on the attribute that is called.

class Entity 
{
     Dictionary<Attribute, Object> attributeRepositoryEnumMap;
     ...
     public dynamic getAttribute(Attribute attribute){
         Object result = attributeRepositoryEnumMap[attribute];
         return result;
     }
}

now I can get return attributes like in the example below without casting

class MyApp
{
    Entity e = new Entity();
    e.setAttribute(Attribute.NAME, "Bob");
    e.setAttribute(Attribute.AGE, 55);
    e.setAttribute(Attrbute.HEIGHT, 5.5f);

    string name = e.getAttribute(Attribute.NAME);
    int age = e.getAttribute(Attribute.AGE);
    float height = e.setAttribute(Attribute.HEIGHT);
}

Not sure how this will all work out for me, but a part of this attempt was to also find an easier work around saving and hydrating my json objects without creating tons of setters and getters.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM