weird (to me) behavior regarding DataContractSerializer constructed class instance not 'returned' from function

StackOverflow https://stackoverflow.com/questions/21343665

  •  02-10-2022
  •  | 
  •  

Frage

Sorry -- this should have been submitted under 'ref' arguments. I was butting my head on on the issue and not really thinking. The answer is that the ModifiedSerialized function should take a ref argument to modify 'target' directly.

ModifySerialized( ref DataClass target ) {...}

Given this runtime.serializable class:

[DataContract]
public class DataClass
{
        [DataMember(Order = 0)]
        public int Number
        {
            get;
            set;
        }

        [DataMember(Order = 1)]
        public string Name
        {
            get;
            set;
        }

        override public string ToString()
        {
            return "DataClass: " + Name + " --  " + Number;
        }
}

and some exercising class:

class Test
{
    public Test()
    {
        DataClass testDataClass = new DataClass() { Name = "Foo", Number = 123 };
        ModifySerialized(testDataClass);
        Console.WriteLine(testDataClass);
    }

    private void ModifySerialized(DataClass target)
    {
        MemoryStream stream = new MemoryStream();
        DataContractSerializer serializer = new DataContractSerializer(typeof(DataClass));
        serializer.WriteObject(stream, new DataClass() { Name = "serialized", Number = 777 });
        stream.Seek(0, SeekOrigin.Begin);
        string sDebug = ASCIIEncoding.ASCII.GetString(stream.GetBuffer());
        Console.WriteLine(sDebug);
        target = (DataClass)serializer.ReadObject(stream);
        Console.WriteLine(target);
    }
}

I'd expect the output to console from within the Test ctr, after the call to ModifySerialized(target) to be something like:

DataClass: name = serialized -- number = 777

But instead, the output after the call to ModifySerialized(target) still shows

DataClass: name Foo number 123 (or similar)

--> (DataClass) Target is not modified

However, inside of the function call to ModifySerialized, the console output is what I'd have expected for 'target' (i.e. serialized, 777).

What am I missing? The parameter to the function is a reference, no? The value of the reference should be modified, no? In contrast, if inside ModifySerialized(target) I just set Number=1234, the value would be correctly output to console after the call from Test as expected.

thanks ...

War es hilfreich?

Lösung

In the ModifySerialized method, you're setting the target parameter to a new instance of DataClass; however, since the parameter is not passed by reference, the testDataClass variable in the Test method still refers to the original instance, which has never been modified. Passing the parameter by reference would produce the behavior you expect.

What am I missing? The parameter to the function is a reference, no? The value of the reference should be modified, no? In contrast, if inside ModifySerialized(target) I just set Number=1234, the value would be correctly output to console after the call from Test as expected.

I think you're confusing two concepts here:

  • value types vs. reference types
  • passing parameters by value vs. by reference

Here, the type of the parameter is a reference type, which means that the value of the parameter is a reference; but the reference is passed by value, so assigning a new instance to the parameter doesn't change the original reference.

For more detailed explanations, I suggest you read Jon Skeet's excellent article: Parameter passing in C#

Andere Tipps

You expect that value of the original variable testDataClass will be changed, but it is not working like this. A copy of its value is passed to method actually and it is chagned during execution, but it can't be reflected to an original variable. For this you should return new value as a method result:

private DataClass ModifySerialized(DataClass target)
{
    MemoryStream stream = new MemoryStream();
    DataContractSerializer serializer = new DataContractSerializer(typeof(DataClass));
    serializer.WriteObject(stream, new DataClass() { Name = "serialized", Number = 777 });
    stream.Seek(0, SeekOrigin.Begin);
    string sDebug = ASCIIEncoding.ASCII.GetString(stream.GetBuffer());
    Console.WriteLine(sDebug);
    target = (DataClass)serializer.ReadObject(stream);
    Console.WriteLine(target);
    return target; // here
}

public Test()
{
    DataClass testDataClass = new DataClass() { Name = "Foo", Number = 123 };
    testDataClass = ModifySerialized(testDataClass);
    Console.WriteLine(testDataClass);
}

Or use ref.

private DataClass ModifySerialized(ref DataClass target)
{
    MemoryStream stream = new MemoryStream();
    DataContractSerializer serializer = new DataContractSerializer(typeof(DataClass));
    serializer.WriteObject(stream, new DataClass() { Name = "serialized", Number = 777 });
    stream.Seek(0, SeekOrigin.Begin);
    string sDebug = ASCIIEncoding.ASCII.GetString(stream.GetBuffer());
    Console.WriteLine(sDebug);
    target = (DataClass)serializer.ReadObject(stream);
    Console.WriteLine(target);
}

public Test()
{
    DataClass testDataClass = new DataClass() { Name = "Foo", Number = 123 };
    ModifySerialized(ref testDataClass);
    Console.WriteLine(testDataClass);
}

Remember that serializer.ReadObject returns a new object that was just read from the stream. It will replace the object remembered by the target variable within the function, but this operation will not change what the outer variable remembers.

For that, try ModifySerialized(ref DataClass target). But still have in mind that it will not "change the object contents". It will forget the old instance and replace it with new instance.

You can check it in the debugger by "Make Object ID" on both to see which variable refers to object #1 and which to #2 etc

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top