Change the same property of different objects without a common baseclass? - c#

I found this thread: How to change the same properties of different objects in one method?
But I have an additional requirement: I don't necessarily have a common baseclass for the objects:
I wish to change "Font" attribute on more objects with a function, but these objects are not originating from a common baseclass.
I'm however sure, that they all have "Font" attribute (or in case not let's drop a compile-time error). How am I supposed to do this with .NET Core7 in C#?
private void RescaleFont<?>(? control_with_font_attribute, double scale) {
control_with_font_attribute.Font = ...;

As #Selvin already implied in their comment:
Use the dynamic keyword.
Objects of type dynamic bypass static type checking and it is assumed that it supports the operation or called attribute.
If there are any errors you would get an exception at runtime. However at compiletime everything will seem "fine".
Your method would look like the following:
private void RescaleFont(dynamic control_with_font_attribute, double scale)
{
control_with_font_attribute.Font = ...;
}
However please note that I'm not quite sure how safe it is to use or if it's acceptable designwise.

There are several options:
The best one - introduce a common base class or interface:
public interface IHaveFont { FontType Font {get;set;}}
private void RescaleFont<T>(T control_with_font_attribute, double scale) where T : IHaveFont
{
control_with_font_attribute.Font = ...;
}
// or
private void RescaleFont(IHaveFont control_with_font_attribute, double scale)
{
control_with_font_attribute.Font = ...;
}
Move the shared functionality to the common method and create bunch of overloads (you can generate them with source generators if needed):
private void RescaleFont(SomeFontTypeA control, double scale)
{
control.Font = Rescale(control.Font, scale));
}
private void RescaleFont(SomeFontTypeB control, double scale)
{
control.Font = Rescale(control.Font, scale));
}
just use source generator to generate all required methods
use dynamic type (the other answer) - no type safety, runtime errors in case of property/field missing or type mismatch (can be mitigated with custom analyzer)
use reflection - hence the dynamic but with ability to improve performance via "caching" it via dynamic compilation of expression trees (see this answer for some inspiration)
// generic so later can cache the reflection
private void RescaleFont<T>(T control, double scale) where T : IHaveFont
{
var pi = typeof(T).GetProperty("Font");
var newValue = pi.GetMethod.Invoke(control, null);
pi.SetMethod.Invoke(control, new [] { newValue });
}
Use expression trees - a bit better type safety, but still possible to have runtime errors (if there is no setter for example) and need to duplicate the getter:
private void RescaleFont<T>(T control, Expression<Func<T, FontType>> fontGetter, double scale)
{
// analyze the fontGetter and use it to generate the setter
}
// and usage
RescaleFont(someControl, x => x.Font, scale)

A. Write it twice
Write the method twice. There should be minimal duplication needed:
private void RescaleFont(Type1 ctrl, double scale)
{
ctrl.Font = GetFont(scale));
}
private void RescaleFont(Type2 ctrl, double scale)
{
ctrl.Font = GetFont(scale));
}
Please note that only setting the font is duplicated and GetFont is not duplicated, it's just called from two places.
B. Add an interface
private void RescaleFont(IWithFont ctrl, double scale)
{
ctrl.Font = GetFont(scale));
}
C. Control.Font Property
Are you sure that your controls aren't inheriting from the same base class, something like System.Windows.Forms.Control?
private void RescaleFont(System.Windows.Forms.Control ctrl, double scale)
{
ctrl.Font = GetFont(scale));
}
D. Use reflection
using System.Linq.Expressions;
using System.Reflection;
class A
{
public Font Font { get; set; } = new Font("Arial", 4);
}
class B
{
public Font Font { get; set; } = new Font("Arial", 3);
}
class C
{
}
static void SetFont<T>(Font toSet, T target, Expression<Func<T, Font>> outExpr)
{
var expr = (MemberExpression)outExpr.Body;
var prop = (PropertyInfo)expr.Member;
prop.SetValue(target, toSet, null);
}
Then:
var exampleFont = new Font("Arial", 11);
var a = new A();
SetFont(exampleFont, a, x => x.Font);
var b = new B();
SetFont(exampleFont, b, x => x.Font);
var c = new C();
SetFont(fontX, c, x => x.Font); // error CS1061: 'Program.C' does not contain a definition for 'Font'

Related

How Can A Non-Abstract Property of a Predefined Abstract Class Be Accessed?

I have populated an array of type ICanvasEffect with objects of derived classes, such as GaussianBlurEffect, PosterizeEffect, etc.
List<ICanvasEffect> effects = new List<ICanvasEffect>();
effects.Add(new GaussianBlurEffect
{
BlurAmount = 4.0f,
BorderMode = EffectBorderMode.Soft,
Optimization = EffectOptimization.Balanced
});
effects.Add(new PosterizeEffect
{
RedValueCount = 3,
GreenValueCount = 3,
BlueValueCount = 3
});
I now want to set the input sources of those effects in an abstract way. For example,
effects[0].Source = inputBitmap;
effects[1].Source = effects[0];
However, in order to call the Source property, I must provide knowledge of the specific class. For example,
(effects[0] as GaussianBlurEffect).Source = inputBitmap;
(effects[1] as PosterizeEffect).Source = effects[0];
How could this be done in an abstract way as in, for example, this fashion?
(effects[0] as effects[0].GetType()).Source = inputBitmap;
(effects[1] as effects[1].GetType()).Source = effects[0];
That's a pretty good example for the Builder Design Pattern. It could looke like this:
using System.Drawing;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
Bitmap inputBitmap = default;
var canvasBuilder = new CanvasBuilder()
.AddEffect(new CanvasEffect1())
.AddEffect(new CanvasEffect2());
Bitmap bitmap = canvasBuilder.Build(inputBitmap);
}
}
public interface ICanvasBuilder
{
ICanvasBuilder AddEffect(ICanvasEffect effect);
Bitmap Build(Bitmap input);
}
public class CanvasBuilder : ICanvasBuilder
{
private List<ICanvasEffect> _effects = new List<ICanvasEffect>();
public ICanvasBuilder AddEffect(ICanvasEffect effect)
{
_effects.Add(effect);
return this;
}
public Bitmap Build(Bitmap input)
{
foreach (var effect in _effects)
{
effect.ApplyEffect(input);
}
return input;
}
}
public interface ICanvasEffect
{
Bitmap ApplyEffect(Bitmap input);
}
public class CanvasEffect1 : ICanvasEffect
{
public Bitmap ApplyEffect(Bitmap input)
{
//modify bitmap
return input;
}
}
public class CanvasEffect2 : ICanvasEffect
{
public Bitmap ApplyEffect(Bitmap input)
{
//modify bitmap
return input;
}
}
dotnetfiddle: https://dotnetfiddle.net/W1UBlL
It would have been smarter if the authors of GaussianBlurEffect, PosterizeEffect etc. had used some common interface which included an IGraphicsEffectSource Source { get; set; } member. Then you could have utilized that interface.
However, that does not seem to be the case.
The syntax you propose:
(effects[0] as effects[0].GetType()).Source = inputBitmap;
(effects[1] as effects[1].GetType()).Source = effects[0];
is not legal, of course. The allowed thing corresponding to this, is:
((dynamic)effects[0]).Source = inputBitmap;
((dynamic)effects[1]).Source = effects[0];
When you use the dynamic keyword, the binding of the member .Source is postponed until run-time. So the C# compiler will produce CIL bytecode that will search for a member .Source, given the run-time type (similar to the .GetType() in your attempt), and if found, will attempt the assignment (which is going to correspond to calling a set accessor).
dynamic has a number of disadvantages, of course. Checks that are normally done when you compile your C#, are with dynamic postponed to run-time, which can lead to fails or slower execution.

How to not write dumb overloads but handle lots of casting in overloaded function

I would like to design something that gets the distance between two things. However, these things can be manifest in many obnoxious forms.
Let's say we have a set of classes. (Not necessarily base and derived)
Cat, CoolCat, ReallyCoolCat
All of them have a way to access a position. I would like to write a function call 'DistanceBetween' that gets the distances between the cats.
I can make overloads:
public static float DistanceBetween(Cat cat1, Cat cat2)
{
return Mathf.Abs(cat1.position - cat2.position);
}
public static float DistanceBetween(CoolCat cat1, CoolCat cat2)
{
return Mathf.Abs(cat1.transform.position, cat2.transform.position);
}
// ... etc...
However, then I would have cases where I need to know the distance between a Cat and a CoolCat or the distance between a CoolCat and a ReallyCoolCat. That means...
public static float DistanceBetween(Cat cat1, CoolCat cat2)
{
return Mathf.Abs(cat1.position, cat2.transform.position);
}
public static float DistanceBetween(CoolCat cat1, ReallyCoolCat cat2)
{
return Math.Abs(cat1.tranform.position, cat2.kittyVariables.position);
}
// ... etc ...
But then it just seems arbitrary cause I can rearrange the order of my arguments and my function wouldn't work. So I have to make...
public static float DistanceBetween(CoolCat cat1, Cat cat2)
{
return Mathf.Abs(cat1.tranform.position, cat1.position);
}
public static float DistanceBetween(ReallyCoolCat cat1, CoolCat cat2)
{
return Math.Abs(cat1.kittyVariables.position, cat2.transform.position);
}
// ... etc ...
So This means the amount of code per cute kitties I make grows by n^2. This amount of code growth is not acceptable due to how many cute kitties I want to make. I cannot implement inheritance because my cute kitties (though similar in name) have very different features and are unique. (I could add doggies and the such too.) So what I am thinking is to create an interface 'IDistanceable' that says the implementing class has a 'Position' property and implementing it in each kitty. But this starts to seem like overkill though, all I wanted was something that can rearrange my arguments and make Func(a,b) equal to Func(b,a)...
I don't really know what to do... both solutions (write 500 functions or make interface and lots of junk) both seem wrong.
The interface will NOT work due to the inability to modify some of the cute kitty classes...
Please help me and my cute kitties! Thanks!
If you can't modify the classes, you're best off wrapping them in something you can modify. That way, you can centralize the class-specific logic in one place (the different constructors).
class CatWrapper
{
private int position { get; set; }
public CatWrapper(Cat cat) { ... }
public CatWrapper(CoolCat cat) { ... }
public CatWrapper(ReallyCoolCat cat) { ... }
public DistanceFrom(CatWrapper other) { ... }
}
This is a purely academic answer, since #Andrews Pilser's is far superior for almost any real-world project, but this will solve it for any class that has any conceivable way of representing a location. It makes heavy use of lambda-expressions, and generics, and requires no control over the underlying classes.
The code was written in LINQPad, so it may look a little odd, but it is standard C# (version 7) that can be snapped right in to Visual Studio. File available here.
This uses a Dictionary to store a ToPointConverter for any Type that can be converted to a Point. A ToPointConverter is created from a static method Create that accepts a lambda that returns a Point from the specific generic T.
As you can see, I provide 3 example "kitty" classes that each store their location in completely different ways. The main function creates a converter for each, storing it in the dictionary of converters, and then calculates the distance between the different combination of "kitties". (I may have gotten my distance function wrong, it's late, but that is a minor detail.)
It produces this output:
2.23606797749979
9.05538513813742
2.23606797749979
8.06225774829855
9.05538513813742
8.06225774829855
void Main()
{
//Define conversion functions for anything that can be converted.
converters.Add(typeof(KittyA), ToPointConverter<KittyA>.Create(kitty => kitty.Location));
converters.Add(typeof(KittyB), ToPointConverter<KittyB>.Create(kitty => new Point { X = kitty.X, Y = kitty.Y }));
converters.Add(typeof(KittyC), ToPointConverter<KittyC>.Create(kitty => kitty.MyLocation));
//Declare some kitties
var kitty1 = new KittyA { Location = new Point { X = 1, Y = 1 } };
var kitty2 = new KittyB { X = 3, Y = 2 };
var kitty3 = new KittyC { MyLocation = new Point { X = 2, Y = 10 } };
//Calculate the distances
GetDistance(kitty1, kitty2).Dump();
GetDistance(kitty1, kitty3).Dump();
GetDistance(kitty2, kitty1).Dump();
GetDistance(kitty2, kitty3).Dump();
GetDistance(kitty3, kitty1).Dump();
GetDistance(kitty3, kitty2).Dump();
}
private Dictionary<Type, IToPointConverter> converters = new Dictionary<Type, IToPointConverter>();
//A helper function that does the converts the passed objects in to Points, and calculates the distance between them.
private double GetDistance(object obj1, object obj2)
{
var point1 = GetConvrterFor(obj1).Convert(obj1);
var point2 = GetConvrterFor(obj2).Convert(obj2);
return Math.Sqrt(Math.Pow(point2.X - point1.X, 2) + Math.Pow(point2.Y - point1.Y, 2));
}
//Another helper that gets the IToPointConverter for the object instance passed in.
private IToPointConverter GetConvrterFor(object obj) => converters[obj.GetType()];
//This generic class stores a lambda expression that converters from T to a Point
public class ToPointConverter<T> : IToPointConverter
{
public static ToPointConverter<T> Create(Func<T, Point> conversion)
{
return new ToPointConverter<T>(conversion);
}
private ToPointConverter(Func<T, Point> conversion)
{
_conversion = conversion;
}
private Func<T, Point> _conversion;
public Point Convert(T obj) => _conversion(obj);
Point IToPointConverter.Convert(object obj) => Convert((T)obj);
}
//The non-generic interface for the converter (so different closed generic types can be stored in the same dictionary, and have their Convert method called.)
public interface IToPointConverter
{
Point Convert(object obj);
}
//Just a standard structure to hold a location. You would use whatever native location class your framework has.
public struct Point
{
public int X;
public int Y;
}
//Some example kitty classes
public class KittyA
{
public Point Location { get; set; }
}
public class KittyB
{
public int X { get; set; }
public int Y { get; set; }
}
public class KittyC
{
public Point MyLocation { get; set; }
}

Why is this method Impure?

I read this answer: https://stackoverflow.com/a/9928643/16241
But I obviously don't understand it because I can't figure out why my method is impure. (The method in question is ToExactLocation()).
public struct ScreenLocation
{
public ScreenLocation(int x, int y):this()
{
X = x;
Y = y;
}
public int X { get; set; }
public int Y { get; set; }
public ExactLocation ToExactLocation()
{
return new ExactLocation {X = this.X, Y = this.Y};
}
// Other stuff
}
Incase you need it here is the exact location struct:
public struct ExactLocation
{
public double X { get; set; }
public double Y { get; set; }
// Various Operator Overloads, but no constructor
}
And this is how I call it:
someScreenLocation = MethodThatGivesAScreenLocation();
if (DestinationLocation == someScreenLocation.ToExactLocation())
{
// Do stuff
}
When I do that, ReSharper flags it with "Impure Method is called for readonly field of value type."
Why is it saying that? And what can I do to make it go away?
It's not pure because it does not return a value dependent only on its input. When the value of X or Y changes so does the return value of ToExactLocation, i.e., its output depends on internal, mutable state.
Additionally, the setters for X or Y in ExactLocation may mutate the input. The getters of ScreenLocation may as well.
someScreenLocation is a readonly field and is a value type. You are calling ToExactLocation on a value, i.e., a readonly field. When you access a reaodnly value type a copy is created as to avoid mutating the value itself. However, your call may mutate that value, which, in many cases, is not what you want as you will be mutating a copy. This is why you get a warning.
In this case, you can ignore it, but I would avoid mutable value types in general.
EDIT:
Let me attempt to simplify...
struct Point
{
int X;
int Y;
bool Mutate() { X++; Y++; }
}
class Foo
{
public readonly Point P;
Foo()
{
P = new Point();
P.Mutate(); // impure function on readonly value type
}
}
When Mutate() is called, a copy of P is created and passed along with the method. Any mutation of P's internal state will be irrelevant as it mutates a copy.
One of the conditions of a Pure Method is that its output (return value) is wholly dependent on its input (arguments).
Your .ToExactLocation() method is not pure, because its output depends both on the input arguments and also on the current value of a mutable struct.
Resharper doesn't like this, because mutable structs are bad (don't use them). I expect the error would go away if you either changed your code to use a class instead of a struct or redesigned the struct so the the .X and .Y members could only be set by the constructor.
Reading the answer, I found out that pure functions are necessarily like functions in mathematics. f(x) = x^2 + 2x + 10 will always return 10 if x is 0.
So ToExactLocation() must return the same values each time it is called, regardless changes to object since initial creation, for it to be called "pure".
There are 2 meaning of "pure function": one theoretical (no side effects/no dependency on mutable state) and another is what ReSharper thinks about functions.
From theoretical point of view your function is not pure because it depends on mutable state. Sample:
var someScreenLocation = new ScreenLocation(1,1);
var locationOne = someScreenLocation.ToExactLocation();
var locationTwo = someScreenLocation.ToExactLocation();
someScreenLocation.X = 3;
var locationThree = someScreenLocation.ToExactLocation();
For method to be pure it can change its result only based on input (not at all as in this case since there is no arguments). But you can clearly observe that locationOne and locationTwo are the same (good sign so far), but unfortunately locationThree is different even if the input (arguments to the function) still the same.
You can make it theoretically pure by making X and Y readonly (and adding constructor).
Even after the change ReSharper will still think it is not pure - to convince it you can use Pure attribute to mark it as pure.
Note that ReSharper marks usage of "impure" functions even in constructor of the class with readonly field. Sample below shows ReSharper warnings:
struct Point
{
public int X;
public int Y;
public Point(int x, int y){X = x;Y = y;}
public void Mutate(){X++;}
public Point TheoreticallyPure(){return new Point(1, 1);}
[Pure] public Point MarkedPure(){ return new Point(1, 1);}
}
class WithReadonlyField
{
public readonly Point P;
public WithReadonlyField()
{
P = new Point();
P.TheoreticallyPure(); // impure function on readonly value type
P.MarkedPure(); // return value of pure not used
P.Mutate(); // impure function on readonly value type - modifies P.
P = new Point().MarkedPure(); // ok to modify P multiple times.
}
public void NormalMethod()
{
P.Mutate(); // impure function on readonly value type, no changes to P
}
}
C# allows modification of readonly fields up to the end of constructor, but ReSharper marks usages of all "impure" functions there too (Note that Mutate function in constructor actually changes value of readonly field P, unlike in NormalMethod where it has no effect).
"readonly... assignments to the fields introduced by the declaration can only occur as part of the declaration or in a constructor in the same class"
Most likely this behavior of ReSharper is for consistency and to avoid cases where moving perfectly valid code changes behavior completely.
It would be better to model this as a static method (on either class) and would get rid of the impure warning. Explanation omitted, as the other answers covers the why already.
Example:
public static ExactLocation ToExactLocation(ScreenLocation loc)
{
return new ExactLocation {X = loc.X, Y = loc.Y};
}
or use an extension method
public static ExactLocation ToExactLocation(this ScreenLocation loc)
{
return new ExactLocation {X = loc.X, Y = loc.Y};
}
Not really sure about the cause, and I'd put this as a comment if it would format correctly...
Wouldn't you want something like:
var someScreenLocation = MethodThatGivesAScreenLocation();
if (DestinationLocation.X == someScreenLocation.ToExactLocation().X &&
DestinationLocation.Y == someScreenLocation.ToExactLocation().Y)
{
// Do stuff
}

Reducing number of very long constructor overloads with just a few different parameters

I made custom interface system which uses basic UI controls like button, label, etc. Some controls have many options, so they use long constructors, and they only differ in one or two parameters. And this is work in progress, so I change optional parameters a lot, and it takes quite some time to apply changes to all constructors.
public Button(string Text, Rectangle Rect, Texture2D Texture, bool moreStuff)
public Button(string Text, Point Position, Texture2D Texture, bool moreStuff)
public Button(string Text, Vector2 Position, Texture2D Texture, bool moreStuff)
I tried using dynamic keyword instead of Rectangle, Point and Vector2 to decrease the number of constructors, and it compiles, works, and seems ok for the moment. But maybe I'm missing something that might break this approach later?
To find out what was passed as dynamic Position I check for .GetType().Name, use a switch and throw an exception at default: if it wasn't a recognized type.
Is it fine to do it like this, or is there a better (more safe or appropriate) way?
Currently it's possible to create a fully customized instance of Button inline, and I wouldn't like to lose that ability.
You don't need to define constructor arguments if you're finding it tedious. You could use an object initializer quite nicely:
SomeButton button = new SomeButton()
{
Text = "",
MoreStuff = false
};
This calls for a parameter object. That's a class with a property per parameter. Your Button constructor would now only take one parameter: That parameter object.
Using dynamic to reduce the number of overloads is definitely not the correct approach.
Using dynamic is not appropritate in your scenario. Having differenct constructor overloads is not a bad thing (not worth than abusing the dynamic keyword). Many classes in .NET Framework BCL has several constructor overloads (for example, FileStream class has 15 contstructors) and some methods has several overload for differenct uses (MessageBox.Show for example).
Another approach is to type this yourself:
class YourPositioningType {
public int X { get; set; }
public int Y { get; set; }
public static YourPositioningType FromVector(Vector2 vector) {
return new YourPositioningType() { X = vector.X, Y = vector.Y };
}
public static YourPositioningType FromRectangle(Rectangle rect) {
// etc..
}
}
Static methods to convert from each of the above types. Then you would call it as:
Button b = new Button("Blah", YourPositioningType.FromVector(new Vector2() { /* etc */));
Then you just use the above class in a single constructor.
If you use object initialisers, then you can set each individual property you require independently. You just have to be careful that the various properties can be initialised independently.
For example:
public class Button
{
public int Width
{
get
{
return Rectangle.Width;
}
set
{
Rectangle.Width = value;
}
}
public int Height
{
get
{
return Rectangle.Height;
}
set
{
Rectangle.Height = value;
}
}
public int X
{
get
{
return Rectangle.Left;
}
set
{
Rectangle.Left = value;
}
}
public int Y
{
get
{
return Rectangle.Top;
}
set
{
Rectangle.Top = value;
}
}
public Rectangle Rectangle
{
get;
set;
}
}
If you had a class like the above, you can do either:
var button = new Button
{
Rectangle = new Rectangle(...)
}
or:
var button = new Button
{
Left = 0,
Top = 0,
Width = 100,
Height = 20
}
Object initializer notation is ideal for initializing object via their properties where you can have multiple combinations of properties needing to be set.

How can I store different objects in a single list

I have two classes an Arc class and a Line class
public class Arc
{
protected double startx;
protected double starty;
protected double endx;
protected double endy;
protected double radius;
public Arc(){}
}
public class Line
{
protected double startx;
protected double starty;
protected double endx;
protected double endy;
protected double length;
public Line(){}
}
But I want to store arcs and lines in the same list, so I tried an interface like this
public interface Entity
{
double StartX();
double StratY();
double EndX();
double EndY();
}
Then I added the appropriate methods to each class and added the code to use the interface. Now I can add both types of objects to a list, but I want to get the length from a line object and don't want to add a length method to the arc or the interface. Is my only option to cast the line object back to a line object like this?
List<Entity> entities = new List<Entity>();
entities.Add(new Line(10,10,5,5));
Line myLine = (Line)Entities[0]
double length = myLine.Length();
*Assuming I have all the proper methods in the line class.
Or is there a better/different way to do this?
If you're in .NET 3.5 or above, you can make this a bit less ugly this way:
List<Entity> entities = new List<Entity>();
// add some lines and some arcs
var lines = entities.OfType<Line>();
Then you just loop through lines, which will contain all the lines (strongly-typed as Lines) and nothing else.
I'm not saying this is the best approach; I'm only saying this is one way to do what you're doing. I agree with Shmoopty that it's an architecture problem.
Since Arc and Line share data (startx and some other fields), I suggest you use a common abstract class as parent class rather than an interface. For example, Figure.
The cast is okay, although I would rather recommend:
Line myLine = Entities[0] as Line;
It will return null if Entities[0] cannot be converted to a Line, rather than throwing an exception. You will be able to check whether myLine is null afterward.
Yes, it is the only way, given your constraints.
I would suggest adding length to the interface (since arc does have a length).
The formula can be found here.
Or alternatively you could add the method to the interface, and have it throw a NotImplementedException.
Have the interface implement a "Size" property (or call it "magnitue", or "Range". . .)
This maps to the Arc's radius, and to the lines length.
Then you can get Entity.Size.
It depends how you want to treat Arcs when you get them out of the list. If you try and cast an Arc to a Line you will get a runtime error, so for starters you should check if the Entity you're working with is a Line.
One way to handle Arcs is to use the Null Object Pattern. It might make sense to add a length method to Arc that returns 0. That way the code that retrieves objects from the list doesn't have to care what kind they are.
List<object> could work depending on what you intend to do with the list. If you have a set number of types you are working with
Lets say I have a list of properties from different classes I need to access. They are all stored in a List of string in format Model.Property.
Then I have a list of objects in a List of objects.
foreach(object model in models)
{
Assembly assembly = Assembly.GetExecutingAssembly();
Type type = assembly.GetType(model.GetType().FullName);
foreach (string match in matches)
{
string property = match.Replace($"{model.GetType().Name}.", "");
if (match == $"{model.GetType().Name}.{property}")
{
PropertyInfo prop = type.GetProperty(property);
string value = prop.GetValue(model).ToString();
}
}
}
Or is there a better/different way to
do this?
If your objects descend from a common class, then you can store them in the same collection. In order to do anything useful with your objects without throwing away type safety, you'd need to implement the visitor pattern:
public interface EntityVisitor
{
void Visit(Arc arc);
void Visit(Line line);
}
public abstract class Entity
{
public abstract void Accept(EntityVisitor visitor);
}
public class Arc : Entity
{
protected double startx;
protected double starty;
protected double endx;
protected double endy;
protected double radius;
public override void Accept(EntityVisitor visitor)
{
visitor.Visit(this);
}
}
public class Line : Entity
{
protected double startx;
protected double starty;
protected double endx;
protected double endy;
protected double length;
public override void Accept(EntityVisitor visitor)
{
visitor.Visit(this);
}
}
Once that's in place, you create an instance of EntityVisitor whenever you need to do something useful with your list:
class EntityTypeCounter : EntityVisitor
{
public int TotalLines { get; private set; }
public int TotalArcs { get; private set; }
#region EntityVisitor Members
public void Visit(Arc arc) { TotalArcs++; }
public void Visit(Line line) { TotalLines++; }
#endregion
}
class Program
{
static void Main(string[] args)
{
Entity[] entities = new Entity[] { new Arc(), new Line(), new Arc(), new Arc(), new Line() };
EntityTypeCounter counter = entities.Aggregate(
new EntityTypeCounter(),
(acc, item) => { item.Accept(acc); return acc; });
Console.WriteLine("TotalLines: {0}", counter.TotalLines);
Console.WriteLine("TotalArcs: {0}", counter.TotalArcs);
}
}
And for what its worth, if your open to trying new languages, then F#'s tagged unions + pattern matching are a handy alternative to the visitor pattern.

Categories

Resources