C
C#16mo ago
Shillelagh

❔ Create new instance of type unknown at runtime?

Say I have a base class, abstract class Letter with child classes A, B, and C. If I have a variable: Letter myLetter which is of unknown type (can be any of A, B, or C, the only guarantee is it's a child of Letter), how can I create a new instance of whatever class myLetter is? I need to create a new instance (must call default constructor as well) of whatever myLetter is and store it in another variable (say myLetter2)
26 Replies
Angius
Angius16mo ago
So, basically,
Letter myLetter = new B();
var thing = CreateInstanceOfWhateverTypeTheVariableIs(myLetter);
thing is B // true
Letter myLetter = new B();
var thing = CreateInstanceOfWhateverTypeTheVariableIs(myLetter);
thing is B // true
?
Shillelagh
ShillelaghOP16mo ago
Yes
Angius
Angius16mo ago
Not gonna lie, I wonder why you'd need to do that, but aight The one and only way I can think of is reflections
Shillelagh
ShillelaghOP16mo ago
Ah, I figured
Angius
Angius16mo ago
Activator.CreateInstance(myLetter.GetType()) should work
MODiX
MODiX16mo ago
Angius
REPL Result: Success
class Letter {}
class B : Letter {}

Letter l = new B();
var x = Activator.CreateInstance(l.GetType());

x is B
class Letter {}
class B : Letter {}

Letter l = new B();
var x = Activator.CreateInstance(l.GetType());

x is B
Result: bool
True
True
Compile: 531.005ms | Execution: 44.365ms | React with ❌ to remove this embed.
Angius
Angius16mo ago
Yeah, seems to work file
Shillelagh
ShillelaghOP16mo ago
Wondering as well if there was another way. In reality I have a reagent clasd which stores properties of the reagent including amount. When I move reagent from one container to another I need to remove the correct quantity from one container (changing its amount variable) and then add a new reagent instance of whatever type the reagent instance is from the first container, set the amount properly and store it I another container
Angius
Angius16mo ago
Uh, ca't you just do
containerOne.Reagents.Add(containerTwo.Reagents[0]);
containerTwo.RemoveAt(0);
containerOne.Reagents.Add(containerTwo.Reagents[0]);
containerTwo.RemoveAt(0);
?
JP
JP16mo ago
So you have
ContainerA | Reagent1(30mg)
ContainerB | --
ContainerA | Reagent1(30mg)
ContainerB | --
And you need to be able to represent a change of state to:
ContainerA | Reagent1(10mg)
ContainerB | Reagent1(20mg)
ContainerA | Reagent1(10mg)
ContainerB | Reagent1(20mg)
Correct?
Shillelagh
ShillelaghOP16mo ago
If I move 100% of the reagent yes, otherwise a new reagent instance is necessary. Yes
JP
JP16mo ago
This sounds like your reagents should not be C# types
Shillelagh
ShillelaghOP16mo ago
Wuh oh
JP
JP16mo ago
I'd have one Reagent type, which has an enum value-ed member ReagentType, and an amount.
Angius
Angius16mo ago
Or should be stored differently, although maybe I'm thinking too much in database terms to be introducing many-to-many lol
JP
JP16mo ago
what happens if you put 2 of the same reagent into a container?
Shillelagh
ShillelaghOP16mo ago
Whenever a reagent is added a method, CombineLikeReagents() which adds their quantities together
JP
JP16mo ago
my quick sketch, might be useful, might not be. It's here :P
public enum ReagentType
{
VeryCoolSubstance,
KindaInterestingSubstance,
SpookySubstance,
}

public class Reagent
{
public required ReagentType Type { get; init; }
public double VolumeMl { get; init; }
}

public class Container
{
private readonly Dictionary<ReagentType, Reagent> _contents = new();

public void Add(Reagent reagent)
{
if (_contents.TryGetValue(reagent.Type, out var existing) {
existing.VolumeMl += reagent.VolumeMl;
} else {
_contents.Add(reagent.Type, reagent);
}

}
}
public enum ReagentType
{
VeryCoolSubstance,
KindaInterestingSubstance,
SpookySubstance,
}

public class Reagent
{
public required ReagentType Type { get; init; }
public double VolumeMl { get; init; }
}

public class Container
{
private readonly Dictionary<ReagentType, Reagent> _contents = new();

public void Add(Reagent reagent)
{
if (_contents.TryGetValue(reagent.Type, out var existing) {
existing.VolumeMl += reagent.VolumeMl;
} else {
_contents.Add(reagent.Type, reagent);
}

}
}
Shillelagh
ShillelaghOP16mo ago
Nice. Yes what I have going on is very similar. My previous version used a class in place of an enum (since different reagents have special properties) but was otherwise identical. Previously all combining was done in an addreagent method as well, but I never made on for the slightly newer version
JP
JP16mo ago
have special properties
What do these properties look like? (Literal C# properties?)
Shillelagh
ShillelaghOP16mo ago
No, methods that define how a reagent might react with another. The base class has a react method, overridden by a child.
JP
JP16mo ago
Aha, I think I have the solution one moment, sketching Each reagent can be responsible for creating the new one:
public abstract class Reagent
{
public double VolumeMl { get; set; }
public abstract void React(Reagent other);
public abstract Reagent Take(double volumeMl);
}

public class Substance1 : Reagent
{
public override void React(Reagent other)
{
// some specific reaction logic
}

public override Reagent Take(double volumeMl)
{
this.VolumeMl -= volumeMl;
return new Substance1
{
VolumeMl = volumeMl,
};
}
}
public abstract class Reagent
{
public double VolumeMl { get; set; }
public abstract void React(Reagent other);
public abstract Reagent Take(double volumeMl);
}

public class Substance1 : Reagent
{
public override void React(Reagent other)
{
// some specific reaction logic
}

public override Reagent Take(double volumeMl)
{
this.VolumeMl -= volumeMl;
return new Substance1
{
VolumeMl = volumeMl,
};
}
}
Shillelagh
ShillelaghOP16mo ago
Ah true That would work
JP
JP16mo ago
If the Take is always the same for all, can do some generic stuff:
public abstract class Reagent<T> where T : Reagent<T>, new()
{
public double VolumeMl { get; set; }
public abstract void React(Reagent other);

public T Take(double volumeMl)
{
this.VolumeMl -= volumeMl;
return new T
{
VolumeMl = volumeMl,
};
}
}

public class Substance2 : Reagent<Substance2>
{
public override void React(Reagent other)
{
// some specific reaction logic
}
}
public abstract class Reagent<T> where T : Reagent<T>, new()
{
public double VolumeMl { get; set; }
public abstract void React(Reagent other);

public T Take(double volumeMl)
{
this.VolumeMl -= volumeMl;
return new T
{
VolumeMl = volumeMl,
};
}
}

public class Substance2 : Reagent<Substance2>
{
public override void React(Reagent other)
{
// some specific reaction logic
}
}
alright, I stop spam now haha
Shillelagh
ShillelaghOP16mo ago
A generic is clever there Nw, thanks for the help
Accord
Accord16mo ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.
Want results from more Discord servers?
Add your server