I recently came across a piece of code at work that has a repeating if-else condition that checks on an enum called OperationType :
public enum OperationType
{ A, B }
Right now the class's job is to run an operation either on device A or on device B, while reading from a SharedDevice and store some values basically for an X,Y plot. We record the characteristics of the SharedDevice in the function of DeviceA or DeviceB. The problem is that we need to iterate over a list of different parameters and send them to the SharedDevice. This list is different for device A and for device B.
Device class:
public class Device
{
public double CurrentValue { get; }
public DeviceParameters Parameters { get; set; }
}
And here is the class responsible for executing this operation:
public class MyOperationExecuter
{
public Device SharedDevice { get; }
public Device DeviceA { get; }
public Device DeviceB { get; }
public List<DeviceParameters> ParametersA { get; }
public List<DeviceParameters> ParametersB { get; }
public List<double> XValuesOfA { get; }
public List<double> YValuesOfA { get; }
public List<double> XValuesOfB { get; }
public List<double> YValuesOfB { get; }
public void DoMyOperation(OperationType operationType)
{
List<DeviceParameters> changingDeviceParameters;
if (operationType == OperationType.A)
{
changingDeviceParameters = ParametersA;
}
else
{
changingDeviceParameters = ParametersB;
}
if (operationType == OperationType.A)
{
XValuesOfA.Clear();
YValuesOfA.Clear();
}
else
{
XValuesOfB.Clear();
YValuesOfB.Clear();
}
foreach (var parameters in changingDeviceParameters)
{
// set the device parameters
SharedDevice.Parameters = parameters;
// retrieve the device readings and store the values in the correct dataprovider
if (operationType == OperationType.A)
{
XValuesOfA.Add(DeviceA.CurrentValue);
YValuesOfA.Add(SharedDevice.CurrentValue));
}
else
{
XValuesOfB.Add(DeviceB.CurrentValue);
YValuesOfB.Add(SharedDevice.CurrentValue);
}
}
// save updated x,y data
Save();
}
}
As you can see there is a repeating if statement which is not very future proof, since we have to check for the enum in every single step. Also we might need to add an C-type device which would result in an ever growing switch statement. We might also need to execute operations on both A and B. How should I refactor this operation so I can keep extending it without this always repeating if-else logic?
A fairly simple way would be to declare a variable representing A or B:
var XValues = operationType == OperationType.A ? XValuesOfA : XValuesOfB;
then you can just use XValues. Do the same for DeviceA. If you have more operations you could use a switch expression.
A neater solution would be to make separate objects containing everything needed for A or B, so your class could simply check the operation type and then delegate all the work to respective object. I.e.
public class MyDevice
{
public Device SharedDevice { get; }
public Device Device { get; }
public List<DeviceParameters> Parameters { get; }
public List<double> XValuesOf { get; }
public List<double> YValuesOf { get; }
public void DoMyOperation()
{
...
}
}
I would also recommend using a single list containing both X and Y values, something like a Vector2. I find this easier to use, and helps avoid repeating code.
Without changing class fields/properties I'd go with new method:
private void SetParameters(List<DeviceParameters> parameters, List<double> xValues, List<double> yValues, Device device)
{
xValues.Clear();
yValues.Clear();
foreach(var parameter in parameters)
{
SharedDevice.Parameters = parameter;
xValues.Add(device.CurrentValue);
yValues.Add(SharedDevice.CurrentValue);
}
}
And then in DoMyOperation it's enough to:
if (operationType == OperationType.A)
{
SetParameter(ParametersA, XValuesOfA, YValuesOfA, DeviceA);
}
else
{
SetParameter(ParametersB, XValuesOfB, YValuesOfB, DeviceB);
}
You should add new class. Which will be used to define device type specific properties.
A class like this;
public class MyDeviceValues
{
public MyDeviceValues(List<DeviceParameters> parameters, List<double> xValuesOf, List<double> yValuesOf)
{
Parameters = parameters;
XValues = xValuesOf;
YValues = yValuesOf;
}
public List<DeviceParameters> Parameters { get; }
public List<double> XValues { get; }
public List<double> YValues { get; }
}
So, you can have a generic DoMyOperation function. It will be like this:
public void DoMyOperation(MyDeviceValues myDeviceValues)
{
var changingDeviceParameters = myDeviceValues.Parameters;
myDeviceValues.XValues.Clear();
myDeviceValues.YValues.Clear();
foreach (var parameters in changingDeviceParameters)
{
// set the device parameters
SharedDevice.Parameters = parameters;
// retrieve the device readings and store the values in the correct dataprovider
myDeviceValues.XValues.Add(DeviceA.CurrentValue);
myDeviceValues.YValues.Add(SharedDevice.CurrentValue);
}
// save updated x,y data
Save();
}
Here is the refactored version of the whole code you pasted:
https://dotnetfiddle.net/dLyJl9
Related
I have the following structure:
public class LogicStatement : ILogicStatement
{
public string TestLogic { get; set; }
public string CompareLogic { get; set; }
public string Operator { get; set; }
public string Expression();
public bool Value();
}
public class Test : ITest
{
public int TestId { get; set; }
public int LiteralId { get; set; }
public string TestName { get; set; }
public string TestText { get; set; }
public string TestDisplayName { get; }
**public ILogicStatement LogicStatement { get; set; }**
public string Expression { get; set; }
public bool Value { get; set; }
}
public class Literal : ILiteral
{
some property members...
**public List<ITest> Tests {get; set;}**
some method members...
}
Note that the class Test has a member of type LogicStatement, and the class Literal has a member of type List.
Note also that all classes have properties and methods that share the same name: Expression, Value, Expression(), Value().
The value of Expression and Value (properties and methods) depend on values in the LogicStatement class.
Throughout the whole project, I use the Interface Type for to instantiate each object to adhere with Dependency Inversion. To support this, I use a factory-like design to create new instances of Test and LogicStatement.
Example:
public static class Factory
{
public static ILogicStatement CreateLogicStatement()
{
return new LogicStatement();
}
public static ITest CreateTest()
{
return new Test(CreateLogicStatement());
}
public static List<ITest> CreateTests()
{
return new List<ITest>();
}
//repeat the same for evey other class.
}
My goal is to have Expression() and Value() be calculated only once in the bottom level class (LogicStatement), and somehow get transfered to their counterpart properties in the higher level classes.
I'm getting the data from Dapper and it looks like all the nested objects are returned from the Dapper module correctly with the same nested structure I intended, and with the right values for all of their members. All of them but Expression, Expression(), Value, Value() are null.
my constructors look like this:
public LogicStatement()
{
Expression();
Value();
}
public Test(ILogicStatement logicStatement)
{
_logicStatement = logicStatement;
Expression = _logicStatement.Expression();
Value = _logicStatement.Value();
}
public Literal(ITest test)
{
_test = test;
Expression = _test.Expression;
Value = _test.Value;
}
and my main:
List<ILiteral> literals = Factory.CreateLiterals();
List<ITest> tests = Facotry.CreateTests();
List<ILogicStatement> logicStatements = Factory.CreateLogicStatements();
literals = GetDataFromDapper();
This last line seems to assign correct values to all other members on all hierarchies. But I cannot get Expression and Value to be anything other than null.
If I test LogicStatement.Expression() and LogicStatement.Value() standalone, they do return the expexted values. but starting at the first parent class Test, these properties are all null.
I think I'm doing something wrong in the way i'm instantiating my objects. Primarily because I'm not sure i understand basic best practices to write constructors.
Maybe I the desired behavior should be implemented through events, where the Test and Literal classes subscribe to changes in the Expression() and Value() methods (or rather to what calculates them). But I never used events and I'd like to know if this fundamentally can be acheived without them first.
My question: How do I make the Expression() Value() at the bottom level class "Fire up" whenever LogicStatement is instantiated, and then have the Expression and Value properties be assigned accordingly as a result.
In other words, I want the following to always be true:
test[i].Expression == literal[i].Expression == LogicStatement[i].Expression()
I'm a beginner in OOP. So any fundamental explanation is welcome.
As you are new to object oriented programming I would start with the basics and leave factories and adhering with Dependency Inversion and the interfaces away for later.
You could tell Dapper to split joined tables into multiple entities (see https://www.learndapper.com/relationships), but for learning OOP I would start doing everything manually.
Your class design does not look proper to me yet. Not sure what Expression and Value of the LogicStatement are, but if they are calculations based on the other properties, I would implement them as (just to show off with complicated words) lazy initialized cached getter properties that are invalidated in the setters of the relevant properties. That ensures you only calculate them once for as many reads you like but recalculate them on first read after one or multiple properties have been updated.
public class LogicStatement {
private string _testLogic;
private string _compareLogic;
private string _operator;
private string? _expression;
private bool? _value;
public LogicStatement(string testLogic, string compareLogic, string #operator) {
_testLogic = testLogic;
_compareLogic = compareLogic;
_operator = #operator;
}
public string TestLogic {
get {
return _testLogic;
}
set {
_testLogic = value;
InvalidateCachedValues();
}
}
public string CompareLogic {
get {
return _compareLogic;
}
set {
_compareLogic = value;
InvalidateCachedValues();
}
}
public string Operator {
get {
return _operator;
}
set {
_operator = value;
InvalidateCachedValues();
}
}
public string Expression {
get {
string? result = _expression;
if (result is null) {
_expression = result = BuildExpression();
}
return result;
}
}
public bool Value {
get {
bool? result = _value;
if (result is null) {
_value = result = EvaluateValue();
}
return result.Value;
}
}
private void InvalidateCachedValues() {
_expression = null;
_value = null;
}
private string BuildExpression() {
//Your logic goes here
throw new NotImplementedException();
}
private bool EvaluateValue() {
//Your logic goes here
throw new NotImplementedException();
}
}
Sorry, it got a bit bigger with the full properties.
In the other classes I would not copy the Value and the Expression but simply remove these properties as anybody can easily access them through the LogicStatement property:
public class Test {
public Test(int testId, int literalId, string testName, string testText, string testDisplayName, LogicStatement logicStatement) {
TestId = testId;
LiteralId = literalId;
TestText = testText;
TestDisplayName = testDisplayName;
LogicStatement = logicStatement;
}
public int TestId { get; }
public int LiteralId { get; }
public string TestName { get; }
public string TestText { get; }
public string TestDisplayName { get; }
public LogicStatement LogicStatement { get; }
}
and the Literal could look like this (I got a bit confused whether this class has one Test or a list of them, I stick to your constructor + properties that hint in the direction of a single one):
public class Literal {
private Test _test;
public Literal(string property1, int property2, Test test) {
Property1 = property1;
Property2 = property2;
_test = test;
}
public string Property1 { get; }
public int Property2 { get; }
public string Expression => _test.LogicStatement.Expression;
public bool Value => _test.LogicStatement.Value;
}
As you decided not to expose the Test in the Literal it makes sense to provide Expression and Value, otherwise they could also be removed (or kept for convenience).
I am experimenting with a nested object class for an upcoming software project, in C#. I know how to do computed fields/properties within a class, at least as far as setting it programmatically with something like the date.
This is a little different. I am setting up a nested class like this:
string Test { get; set; }
List<Line> Detail { get; set; }
decimal Total {
get {
return TotalOf();
}
}
decimal TotalOf() {
var listQuery = this.Detail;
// This is where I'm trying to figure out how to do.
// I want the TotalOf function to return the sum of the
// Cost fields of the contained list items (Line is defined below).
// I will remove the "return 0;" eventually once
// I can figure out how to do the calculation.
return 0;
}
public class Line {
int indexOf { get; set; }
decimal Cost { get; set; }
}
That way, the field Total is automatically calculated rather than me having to compute it through the code consuming this class.
I have tried searching all over but I can't seem to find the right answer. I have plenty of time to do this, and worst case, I can just do it in the program consuming this class, but I thought I'd ask. When I hit the . after typing in this.Detail, the only aggregate function that comes up is Count.
I have tried to use the Detail.Sum function, hoping the Linq would bring up a lambda expression that I could then say "add up the Cost" but it won't come up.
I know this should be simple but I can't figure it out.
First, set access modifiers for Line properties like as public or other. Because, on default state it is private.
public class Line
{
public int indexOf { get; set; }
public decimal Cost { get; set; }
}
Then, set up root class like as LineCollection.
public class LineCollection
{
public class Line
{
public int indexOf { get; set; }
public decimal Cost { get; set; }
}
public string Test { get; set; }
public List<Line> Detail { get; set; }
public decimal Total { get; set; }
}
On LineCollection initialize default values for properties on constructor:
public class LineCollection
{
public class Line
{
public int indexOf { get; set; }
public decimal Cost { get; set; }
}
public string Test { get; set; }
public List<Line> Detail { get; set; }
public decimal Total { get; set; }
public LineCollection()
{
this.Test = string.Empty;
this.Detail = new List<Line>();
}
}
After this modify get/set accessors for Total property. I guess, property is read only and we not need to define set accessor.
public decimal Total
{
get
{
return this.Detail.Sum(x => x.Cost);
}
}
Code in get accessor automatically runs when we trying to get his value. Finally, we can run tests for checks.
LineCollection collection = new LineCollection();
collection.Detail.Add(new LineCollection.Line() { indexOf = 0, Cost = 43.3m });
collection.Detail.Add(new LineCollection.Line() { indexOf = 1, Cost = 23 });
collection.Detail.Add(new LineCollection.Line() { indexOf = 3, Cost = 56.21m });
Console.WriteLine(collection.Total.ToString());
It returns 122,51.
Think this method would work for you:
decimal TotalOf() {
return this.Detail.Select(line => line.Cost).Sum();
}
I believe this way also works:
decimal TotalOf() {
return this.Detail.Sum(line => line.Cost);
}
Hope this helps :)
Let me describe a simplified story on this. I try to design a recommend function, it will recommend you different product by different match rules.
// TV will match a product by given TV related parameters
public class TvMatcher
{
public Product Match(ITvMatchParam tvMatchParam)
{
return new Product(); // pick a tv by tvMatchParam
}
}
public interface ITvMatchParam
{
int Age { get; }
int Gender { get; }
}
// Phone will match a product by given Phone related parameters
public class PhoneMatcher
{
Product Match(IPhoneMatchParam phoneMatchParam)
{
return new Product(); // pick a phone by phoneMatchParam
}
}
public interface IPhoneMatchParam
{
int Age { get; }
int Education { get; }
}
The Recommend here is a public API which accept a union parameters.
Then it will pick a Matcher and call match() to pick a product.
public class Main
{
public Product Recommend(AllMatchParams allParams)
{
// pseudo code here, can't compile
IMatcher matcher = CreateMatcherFactoryMethod(allParams);
return matcher.Match(allParams);
}
private static IMatcher CreateMatcherFactoryMethod(AllMatchParams allParams)
{
IMatcher matcher;
if (allParams.Product == 0)
{
matcher = new TvMatcher();
}
else
{
matcher = new PhoneMatcher();
}
return matcher;
}
}
class AllMatchParams : ITvMatchParam, IPhoneMatchParam
{
public int Age { get; }
public int Education { get; }
public int Gender { get; }
public int Product { get; }
}
But above code won't be compiled because TvMatcher and PhoneMatcher cannot implement IMatcher
public interface IMatcher
{
Product Match(AllMatchParams allMatchParams);
}
Since TvMatcher only cares Tv related parameters, PhoneMatcher only cares PhoneMatcher related parameters, I don't want to force PhoneMatcher implment IMatcher.
public class PhoneMatcher: IMatcher
{
Product Match(AllMatchParams allParams)
{
// don't like this because PhoneMatcher only cares subset properties in allParams
}
}
So I try to implement an AbstractClass and make PhoneMatcher inherit it. I hope it can accept a AllMatchParams and only see PhoneMatchParam in implementation.
public abstract class AbstractMatcher<TProductMatchParam>: IMatcher
{
public Product Match(AllMatchParams allMatchParams)
{
return ProductMatch(allMatchParams); // fails here
}
protected abstract Product ProductMatch(TProductMatchParam param);
}
public class PhoneMatcher: AbstractMatcher<IPhoneMatchParam>
{
protected Product ProductMatch(IPhoneMatchParam phoneMatchParam)
{
// ...
}
}
public class TvMatcher: AbstractMatcher<ITvMatchParam>
{
protected Product ProductMatch(ITvMatchParam tvMatchParam)
{
// ...
}
}
But above code won't compile because AllMatchParams cannot cast to generic variable <TProductMatchParam>.
Is there a way to make AbstractMatcher.Match can accept a fixed type parameter and delegate to child, so child can see different interfaces of that parameter?
I had tried to add constraints to generic variable but fail because cannot make TProductMatchParam constraint to either IPhoneMatchParam or ITvMatchParam at same time.
Your AbstractMatcher is a bit too much
You could make your IMatcher interface generic instead :
public interface IMatcher<T>
{
Product Match(T matchParam);
}
public class PhoneMatcher: IMatcher<IPhoneMatchParam>
{
public Product Match(IPhoneMatchParam matchParam)
{
// ...
}
}
public class TvMatcher: IMatcher<ITvMatchParam>
{
public Product Match(ITvMatchParam matchParam)
{
// ...
}
}
In this case you can pass your AllMatchParams instance in the 2 methods.
return new PhoneMatcher().Match(criteria) ?? new TvMatcher().Match(criteria);
Will returns the first not null matched product
With the help of everyone in a very short time. The problem got solved by overriding the toString Method.
I have a problem with the following: (solved)
public class CryptoApiResponse
{
[DeserializeAs(Name = "ticker")]
public List<CryptoAttributes> CryptoCurrency { get; set; }
public override string ToString()
{
return $"Currency:{CryptoCurrency[0].Currency} " +
$"PriceFiat:{CryptoCurrency[0].PriceFiat} " +
$"Fiat:{CryptoCurrency[0].TargetFiat}";
}
}
public class CryptoAttributes
{
[DeserializeAs(Name = "base")]
public string Currency { get; set; }
[DeserializeAs(Name = "target")]
public string TargetFiat { get; set; }
[DeserializeAs(Name = "price")]
public string PriceFiat { get; set; }
}
And I want to access the following:
public void Display<CryptoApiResponse>(List<CryptoApiResponse> apiList)
{
if (apiList != null)
{
foreach (CryptoApiResponse cryptoCurrency in apiList)
{
Console.WriteLine(cryptoCurrency.ToString());
}
}
Console.ReadLine();
}
Console.WriteLine(obj);
// this means more or less the following
Console.WriteLine(obj.ToString());
// this means you should override the ToString() method
// or to make a custom string
You're iterating through a List, and in each crypto there exist a sub-list List. In short you get List>.
When you foreach this List, you may need to use a second foreach to iterate the values in the Sub list to reach your property.
foreach (var crypt in crypto)
{
foreach (var basedata in crypt.Ticker)
{
Console.WriteLine($"Currency:{basedata.Currency} Price: {basedata.Price} Target: {basedata.Target}");
}
}
If you keep the naming of the API you linked and differentiate between lists ans single objects names it will be easier to understand what is the problem. The classes should look something like this (pay attention to the difference between Ticker and Tickers
public class Crypto
{
public List<Ticker> Tickers { get; set; }
}
public class Ticker
{
public string Currency { get; set; }
public string Target { get; set; }
public string Price { get; set; }
}
The parameter crypto (should be cryptos) in Display is a list and Tickers is a list, so you need nested loop. You should also remove the Crypto parameter from the methos signature as it hides the Crypto class
public void Display(List<Crypto> cryptos)
{
foreach (Crypto crypto in cryptos)
{
foreach (Ticker ticker in crypto.Tickers)
{
Console.WriteLine(ticker);
}
}
}
Or if you would like to use partial Linq
public void Display(List<Crypto> cryptos)
{
foreach (Ticker ticker in cryptos.SelectMany(crypto => crypto.Tickers))
{
Console.WriteLine(ticker);
}
}
Could you try to use "Crypto" instead of "var" when looping? I mean do it like this. I remeber the version before VS2015 (may be VS2010), the type of variable will be treated as object if we use "var".
public void Display<Crypto>(List<Crypto> crypto)
{
if (crypto != null)
{
// Currency, Target and Price
foreach (***Crypto*** ticker in crypto)
{
Console.WriteLine(ticker); // ticker Type Crypo
// ticker.Ticker
}
}
Console.ReadLine();
}
I'm trying to accomplish some hacking & slashing and was wondering how to approach the following.
There are 2 interfaces defined:
public interface IBase
{
string Name { get; }
void Run();
}
public interface ISecondBase<T> : IEntityTask
{
Thing<T> Thing { get; }
}
Somewhere else I have a list of IBase.
This list is filled ISecondBase. I would like to be able to loop through the list of Base, but using some reflection tricks and hacks als be able to call Thing on the items. I know they're there, the compiler doesn't.
So I'd have to cast it to its concrete type at runtime, but this cast has to be dynamic, based on reflected information in the loop... So all type information is dynamic... I'm starting to think in circles :)
Since I know on beforehand that everything inside it is always of the SecondBase type, I decided to use the dynamic keyword and just let it resolve at runtime. This seems to me like an easy way out. Is there some best practice for these cases? Should I redesign, without loss of generality, and how?
foreach(var x in y)
{
dynamic melp = x;
melp.Thingy;
}
Where to start?
Edit: Perhaps some more code to make the example less contrived.
I have the base classes as mentioned. In real life they look like this:
public interface IEntityTask
{
string Name { get; }
void Run();
}
public interface IEntityTask<T> : IEntityTask
{
Task<T> Task { get; }
}
//Then there are classes that implement these:
public class CreateEntityTask<T> : IEntityTask<Guid>
{
public T Entity { get; private set; }
public Func<T, Guid> EntityMethod { get; private set; }
public Task<Guid> Task { get; private set; }
public void Run()
{
Task = Task<Guid>.Run(() => entityAccess.CreateEntity<T>(Entity, EntityMethod));
}
}
public class ReadEntityTask<T> : IEntityTask<T>
{
public Guid EntityId { get; private set; }
public Func<Guid, T> EntityMethod { get; private set; }
public Task<T> Task { get; private set; }
public void Run()
{
Task = Task<T>.Run(() => entityAccess.ReadEntity<T>(EntityId, EntityMethod));
}
}
//Furthermore there is a class called EntityTaskManager, which holds a list of these things and runs, awaits & collects the results on them.
public class EntityTaskManager
{
public List<IEntityTask> EntityTasks { get; set; } // I want tasks of Guid and bool in here!!!!
public Dictionary<string, object> EntityTaskResults { get; set; }
}
In a calling class I construct a new EntityTask and add it to the list. And then call RunTasks on the manager.
I'd modify IEntityTask like this:
public interface IEntityTask
{
string Name { get; }
void Run();
object Result { get; }
}
If EntityTaskManager is the only place, where you work with IEntityTask type, the implementation of Result would be explicit:
public class CreateEntityTask<T> : IEntityTask<Guid>
{
/* The rest of code here */
object IEntityTask.Result
{
get { return Task.Result; }
}
}
Then fetching task results should be trivial:
var results = entityTasksManager
.EntityTasks
.Select(t => t.Result);