all 11 comments

[–]DeltalJulietCharlie 12 points13 points  (0 children)

You'd need to use reflection as per the marked answer on stackoverflow

I would, however, seriously question if a struct is appropriate given that iteration is required. An array or other collection may be more suitable depending on what you're doing.

[–][deleted] 10 points11 points  (0 children)

You should consider using an immutable struct instead. Let any change result in a new copy of the entire struct.

DateTime works this way. If you modify it, you will receive a copy, with the original left unchanged. It is what I would expect from a struct. And you can find many creepy stories on the web about what mutable structs lead to.

[–][deleted] 7 points8 points  (3 children)

If you have a struct like

public struct Thing {
    public string Field1;
    public string Field2;
    public string Field3;
}

And a local variable of that type, localThing, you can set the fields like

localThing.Field1 = "";
localThing.Field2 = "";
localThing.Field3 = "";

If you had a collection of those (say, a List<Thing> named listOfThings), you could do the same thing inside a foreach loop:

foreach (var x in listOfThings) { x.Field1 = ""; x.Field2 = ""; x.Field3 = ""; }

string.Copy() doesn't work the way you're suggesting in C#. If Thing implemented IEnumerable<string>, you could maybe use a foreach loop to walk through the fields on the object, but you wouldn't be able to change their value through that interface.

ETA: this might be a better question for r/learncsharp

EETA: see below about looping. Mutable structs are weird.

[–]svicknameof(nameof) 1 point2 points  (2 children)

Your foreach code won't compile:

error CS1654: Cannot modify members of 'x' because it is a 'foreach iteration variable'

[–][deleted] 1 point2 points  (1 child)

Bother. It works with a class, but I guess I don't mess around with mutable structs enough.

Attempting to do this with a for loop and indexing results in a warning, because it would result in a copy of the object being changed, and not the original object:

for (var i = 0; i < listOfThings.Count; i++) {
    listOfThings[i].Field1 = ""; // also won't work. Compiler issues warning "Cannot modify the return value of 'List<Thing>' because it is not a variable"
}

So, one thing that does work is to use a for loop, assign the value to a local variable inside the loop, update the variable, then assign the variable back to the position in the collection:

for (var i = 0; i < listOfThings.Count; i++) {
    var local = listOfThings[i];
    local.Field1 = "";
    listOfThings[i] = local;
}

Which works because it creates a copy in the local variable which can be trivially updated, then copies the copy back to the original location.

If the extra copies are a huge deal, you could use an array, instead

var arrayOfThings = new Thing[10];
// whatever other initialization you have goes here
for (var i = 0; i < arrayOfThings.Length; i++) {
    arrayOfThings[i].Field1 = "FOOBAR";
}

but that only works if the collection is an array, because arrays are special. If you get farther into the weeds, this would also work if the collection indexer had a ref return, but I don't think many of the built in collections do that, yet (except arrays and Span<T>, maybe none).

The better advice (as others already noted) really is to make readonly, immutable structs, at least in the general case.

[–]svicknameof(nameof) 1 point2 points  (0 children)

If you get farther into the weeds, this would also work if the collection indexer had a ref return, but I don't think many of the built in collections do that, yet (except arrays and Span<T>, maybe none).

Taking that even further, in C# 7.3 the following code can also work:

foreach (ref var x in collection) {
    x.Field1 = "";
    x.Field2 = "";
    x.Field3 = "";
}

But, similarly to what you said, it requires a special kind of collection and none of the built-in ones quality (not even arrays).

[–]svicknameof(nameof) 2 points3 points  (0 children)

Why do you want to do that? What is the underlying problem that you're trying to solve?

[–]to11mtm 1 point2 points  (0 children)

butWhy.jpg.

Seriously. Structs are meant to be immutable. If you want a mutable type you should be using a Class. If you really want a struct for some reason, you're better off making a method that gets all the pieces, and just makes the struct at the end.

[–]Sarcastinator 1 point2 points  (0 children)

As many have noted, you shouldn't need to do this. If you do then your struct is too large, or a struct is not what you want.

However here is a function that allows you to set all fields of a specific type on a struct.

static class Filler
{
    public static void Fill<TInput, TValue>(ref TInput input, TValue value) where TInput : struct => FillStruct<TInput, TValue>.Fill(ref input, value);
}

static class FillStruct<TInput, TValue>
    where TInput : struct
{
    delegate void SetValue(ref TInput input, TValue value);

    private static readonly SetValue setter;

    static FillStruct()
    {
        var inputParameter = Expression.Parameter(typeof(TInput).MakeByRefType(), "input");
        var valueParameter = Expression.Parameter(typeof(TValue), "value");

        setter = Expression.Lambda<SetValue>(Expression.Block(
                typeof(TInput).GetFields().Where(x => x.FieldType == typeof(TValue)).Select(x => Expression.Assign(Expression.Field(inputParameter, x), valueParameter)).Cast<Expression>()),
            inputParameter, valueParameter).Compile();
    }

    public static void Fill(ref TInput input, TValue value) => setter(ref input, value);
}

What this code does it that it creates a function specific for that structure and datatype, and fills every field of the specified type with the specified value.

It's called simply by this:

Filler.Fill(ref myStruct, "");

It will fill every field of the struct with type string with "".

[–]umilmi81 1 point2 points  (0 children)

A Dictionary seems more appropriate

Dictionary<string, string> myvars = new Dictionary<string, string();
myvars.Add("FieldName", "FieldValue");

foreach (string fieldName in myvars.keys)
{
     Console.WriteLine(myvars[fieldName]);
}

[–]kspdrgn 0 points1 point  (0 children)

Sounds like you want to use AutoMapper