"Dereference" var in C#
I have some code in C# that uses a structure as such:
ArrayList addrs = new ArrayList();
byte[] addr = new byte[8];
while (oneWire.Search_GetNextD开发者_如何学Goevice(addr))
{
addrs.Add(addr);
}
In this example, every element in the ArrayList
is the same as the last device that was found because it would appear as though addr
is passed out by reference and I am simply copying that reference into the ArrayList
.
Is there any way to "Dereference" addr
to only extract it's value?
It's also possible my assessment of the situation is incorrect, if that appears to be the case, please let me know
Thanks!
If addr
is a class variable (and not a struct) it would figure.
In that case Search_GetNextDevice()
would be filling the same instance over and over.
You might be able to solve it with something like
byte[] addr = new byte[8];
while (oneWire.Search_GetNextDevice(addr))
{
addrs.Add(addr);
addr = new byte[8];
}
updated with the byte[]
info
You probably need something like addrs.Add(addr.Clone());
to create a copy (or clone) of addr
to put in your list.
if addr is a structure, then there are multiple problems can arise.
First of all, addr will not change and if it has to change then you need to declare Search_GetNextDevice
as a ref.
If you are changing values of addr.something
in Search_GetNextDevice
, then you will notsee those values in the copy you have.They will be lost since structures are passed by value to the methods.
Convert to array, then loop that copy:
ArrayList addrs = new ArrayList();
while (oneWire.Search_GetNextDevice(addr.ToArray((whateverTypeYourItemsHave[])typeof(whateverTypeYourItemsHave))))
{
addrs.Add(addr);
}
Or better if you know what the items in your list are, use generic list. Better to read, less error prone:
List<whateverYourItemTypeIs> addrs = new List<whateverYourItemTypeIs>();
while (oneWire.Search_GetNextDevice(addr.ToArray()))
{
addrs.Add(addr);
}
With reference types, you ultimately have to clone the object. There is not a built-in way for most classes, though there is for an array, which the Accepted Answer shows appropriately.
One option (for those not using an array) is to serialize and deserialize from a format like JSON or MessagePack. This works for some types. (a POCO class can be serialized to JSON, and can IEnumerables). You can use a method like this to clone an object via serialization:
internal static class CloneHelper
{
public static T Clone<T>(this T item) where T : class, new()
{
var asString = JsonSerializer.Serialize(item);
var newObject = JsonSerializer.Deserialize<T>(asString);
if (newObject is null)
{
throw new Exception("Something went wrong in serialization");
}
return newObject;
}
}
If you want to simulate C# 9 record cloning functionality (with
keyword), you can do something like this (probably don't do this in production).
internal static class CloneHelper
{
public static T Clone<T>(this T item, object? with = null) where T : class, new()
{
var asString = JsonSerializer.Serialize(item);
var newObject = JsonSerializer.Deserialize<T>(asString);
if (newObject is null)
{
throw new Exception("Something went wrong in serialization");
}
if (with is not null)
{
foreach (var property in with.GetType().GetProperties())
{
var val = property.GetValue(with);
typeof(T).GetProperties()
.FirstOrDefault(x => x.Name == property.Name
&& ReferenceEquals(x.PropertyType, property.PropertyType))
?.SetValue(newObject, val);
}
}
return newObject;
}
}
However, serialization and reflection are both slow and not 100% type-safe, so don't do this in performance critical or safety critical situation.
精彩评论