-4

(Sorry if this is a duplicate, but I've been doing a lot of searching and haven't found an answer)

I'm currently working on a object save/load system that allows me to save data between sessions, namely settings and the like. My current 'saver' requires me to input a string for the name of the setting, which isn't too much work. However, I'm starting to expand the system, and I'm going to be saving tens if not hundreds of objects (not often though). Now, having to pass the name as a parameter is very tricky now, because I'm using for loops, so unless I want to create a new list for the names, I need a better method.

EDIT (Added example of what I want):

    void LogName(object obj)
{

    Debug.Log(GetNameOfParam(obj));
    DoStuff();
}
LogName(ObjectToSave);

This should log "ObjectToSave" not "obj"

I've tried many different solutions:
obj.GetType().Name, which simply outputs the Type name
nameof(obj), which outputs "obj" and even

public static string GetName<T>(Expression<Func<T>> expr)
    {
        MemberExpression body = ((MemberExpression)expr.Body);
        string name = body.Member.Name;
        //object value = ((FieldInfo)body.Member).GetValue(((ConstantExpression)body.Expression).Value);
        return name;
    }
    GetName(() => obj)

which gives me "obj" as well.

EDIT (Added code): Overwrite code:

private void OverwriteObject(object obj)
{
    string name = obj.GetType().Name;
    Debug.Log($"GetType.Name:{obj.GetType().Name}");
    Debug.Log($"nameof:{nameof(obj)}");
    Debug.Log($"GetName<T>:{ObjectExtensions.GetName(() => obj)}");

    string path = Paths.SettingsSaveLocation + Paths.SettingFilePrefix + name + Paths.SettingFileSuffix;
    //If setting file exist, read it
    if (File.Exists(path))
    {
        try
        {
            using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
            {
                //Create buffer and read data
                byte[] data = new byte[fs.Length];
                fs.Read(data, 0, (int)fs.Length);
                //Load data into setting object
                JsonUtility.FromJsonOverwrite(Encoding.UTF8.GetString(data), obj);
            }
        }
        catch (Exception e)
        {
            Debug.LogException(e);
        }
    }
    //Otherwise, create a new one
    else
    {
        WriteObj(name, obj);
    }
}

Write code:

    private void WriteObject(string name, object obj)
{
    string path = Paths.SettingsSaveLocation + Paths.SettingFilePrefix + name + Paths.SettingFileSuffix;
    using (FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
    {
        //Use prettyPrint so it looks nicer
        string data = JsonUtility.ToJson(obj, true);
        fs.Write(Encoding.UTF8.GetBytes(data), 0, data.Length);/**/
    }
}

For loop (incomplete, needs work):

foreach (var obj in GameObject.FindGameObjectsWithTag("Enemy"))
    {
        WriteObject("Object name is xyzasd123", obj);
    }
Eternal
  • 117
  • 1
  • 9
  • 4
    Possible duplicate of [Finding the variable name passed to a function](https://stackoverflow.com/questions/72121/finding-the-variable-name-passed-to-a-function) – SᴇM Jun 10 '19 at 07:51
  • _"nameof(obj), which outputs `obj`"_ -So what you expect it to output? If your parameter name is `obj` then isn't that what you were asking? – SᴇM Jun 10 '19 at 07:52
  • @SᴇM "obj". But I'm just adding it in so that people don't think I'm a lazy person who hasn't tried anything – Eternal Jun 10 '19 at 07:53
  • 2
    I'm very confused... what answer, if not "obj", are you looking to get? – Marc Gravell Jun 10 '19 at 07:54
  • Please add the code that calls this method, so we can see the parameter. – Reinstate Monica Cellio Jun 10 '19 at 07:54
  • @MarcGravell I'm calling SaveObj(setting) or SaveObj(xyzasd), do I want it to return setting or xyzasd – Eternal Jun 10 '19 at 07:57
  • @Everts because I'm doing it in a for loop. Now, how would I go about saving a hundred objects without writing it by hand? – Eternal Jun 10 '19 at 07:59
  • _"do I want it to return setting or xyzasd"_ - is that a question? – SᴇM Jun 10 '19 at 08:00
  • 1
    What's wrong with the link in the first comment? That shows how to do exactly what you're asking. – Reinstate Monica Cellio Jun 10 '19 at 08:00
  • Are you after the game object name? Then you should use WriteObject(obj.name, obj); but that does not guarantee to be unique. GetInstanceID would. It depends on what you are willing to store. – Everts Jun 10 '19 at 08:25
  • @Everts The thing is I will also call it for my settings, which is where I'm currently testing it. For them, it's actually a static class instance, which doesn't have a name field. Anyway, visual studio won't even let me add `obj.name` – Eternal Jun 10 '19 at 08:28
  • What exactly are you trying to save? most of this sounds like you'd do better with scripted objects – BugFinder Jun 10 '19 at 08:37
  • If you have a class with the state information in it, then you can just serialise that and it will retain the field names. – Neil Jun 10 '19 at 08:44
  • @BugFinder In my main script that's attached to a gaeobject, I have many static instances of other classes. E.g. `public static Graphics Graphics = new Graphics();` I'm using `JsonUtility.ToJson and FromJson<>` for my saving and loading. The problem is, I'm using the object name as my save location (see code above). Now when I do that to GameObjects, of which I have far more... – Eternal Jun 10 '19 at 08:52
  • @Neil I've tried, but I can't because static fields and properties aren't serialized :( – Eternal Jun 10 '19 at 08:53
  • You can serialise static properties like this: https://stackoverflow.com/questions/30193871/how-to-serialize-static-properties-in-json-net-without-adding-jsonproperty-att – Neil Jun 10 '19 at 09:45
  • @Neil I've tried JSON.Net and when I try to Serialize the main (MonoBehaviour) class, I either get self-referencing loops or crashes when I resolve the loops with a JsonSerializerSetting parameter. So I can't do that. Anyway, I'm using JsonUtility (built in to unity) – Eternal Jun 10 '19 at 09:48
  • Is there a way to convert your 'view model' into a simpler 'serialisation model' that WILL serialise? That way to you won't have static properties or circular dependencies. – Neil Jun 10 '19 at 09:51
  • @Neil The problem is, I'm using a MonoBehaviour and some of the built in functions to ensure things like Singletonality (if that's a word) and other things. This means that the base class has some referencing variables that I have no control over whatsoever, unless I remove the MonoBehaviour, which would break my game. – Eternal Jun 10 '19 at 09:53
  • I meant creating a model just for loading and saving. You can keep the existing model for in-game use, just provide a way to translate that into something you can serialise. – Neil Jun 10 '19 at 10:08
  • This isn't possible, but, you could perhaps take an Expression to your method, and inspect that at runtime to lift out the name of the referenced member being passed along. Honestly though, if you're having to do it this way, chances are your design is wrong. – Clint Jun 10 '19 at 11:57
  • 1
    @RowanRadosav-McRae You can't use Json Dot Net to serialize monobehavior classes without first setting up a custom Contract Resolver and Converter. – Draco18s no longer trusts SE Jun 10 '19 at 13:18

1 Answers1

0

If it is not the type name you are looking for and nether the reflected parameter name of your method, then you need to store the name in a public property in each object.

For example create a interface called IObjectName.

public interface IObjectname
{
    string ObjectName { get; set; }
}

Each object that should have a name, should implement this interface.

Now you could change your method to something like this:

void LogName(IObjectName obj)
{
    Debug.Log(obj.Name);
    DoStuff();
}

Or you could cast the parameter IObjectName, but this will throw an InvalidCastException if the type doesn't implement the interface.

For example:

void LogName(object obj)
{
    // Risk that InvalidCastException will be thrown.
    Debug.Log((obj as IObjectName).Name);
    DoStuff();
}

Hopefully this solves your problem.

jooni91
  • 64
  • 2
  • 3