I want to be able to create "Transformation" classes that take a given object, perform a series of transformations on it (i.e. change property values) and keeps track of the transformations performed. The transformation performed will vary based on the properties of the object provided.
I want to be able to apply transformation rules (which are finite and commin) within a given transformation class using a fluent style interface.
At a high level, I understand that I will likely have an ITransformer, an ITransformationRule, and ITransformationResult, and a few other objects to make this happen.
How I would want the code to work when creating Transformation classes...
public OfflinePersonToOnlineTransformation : TransformerBase<Person>
{
public OfflinePersonToOnlineTransformation()
{
Transform(x => x.PersonClassification)
.WhenCreatedBefore("1/1/2000")
.ClassifyAs("Online");
}
}
I understand that my TransformerBase would need to implement the "Transform" method that takes a Func or Expression, and I understand that it would need to keep a collection of ITransformationRules. I also understand that I would likely use Extension methods for the "WhenCreatedBefore" and "ClassifyAs" methods.
The trouble is, I can't figure out how to make it all work. I've looked at source code for Fluent Validation .NET as it does validation this way, but the complexity is killing me. I'm looking for a tutorial that covers this, or someone to spell it out in a way that is a pretty straightforward.
Thanks in advance.
Not quite sure why you want to go to all this effort when linq does most of it for you:
IEnumerable<Person> somePeople; // from wherever
somePeople.Where(x => x.CreateDate < new DateTime(2000,1,1))
.ForEach(x => x.PersonClassification = "Online");
Simply by adding the ForEach from here noting the proisos for why it's not included by default.
If you want to make the WhereCreatedBefore nicer then a simple extension like so:
static class PersonExtensions
{
public static bool WhereCreatedBefore(this Person p,
int year, int month, int day)
{
return p.CreateDate < new DateTime(year,month,day);
}
}
which is useful in and of itself and gives you:
somePeople.Where(x => x.CreatedBefore(2000,1,1))
.ForEach(x => x.PersonClassification = "Online");
Why limit yourself when simply expanding on the tools linq gives you makes things easier.
If you want to chain multiple side effects a simple alteration of ForEach like so:
public static IEnumerable<T> Modify<T>(
this IEnumerable<T> input, Action<T> action)
{
foreach (var x in input)
{
action(x);
yield return x;
}
}
giving you:
somePeople.Where(x => x.CreatedBefore(2000,1,1))
.Modify(x => x.PersonClassification = "Online");
.Modify(x => x.LastModifiedBy = Environment.UserName);
Or if you use the language integrated part of it:
(from p in somePeople where p.CreatedBefore(2000,1,1)) select p)
.Modify(p => p.PersonClassification = "Online");
.Modify(p => p.LastModifiedBy = Environment.UserName);
IF you really* wanted to you could write a ClassifyAs extension like so:
public static IEnumerable<Person> ClassifyAs(
this IEnumerable<Person> input, string classification)
{
foreach (var p in input)
{
p. PersonClassification = classification;
yield return p;
}
}
giving you your original of:
(from p in input where p.CreatedBefore(2000,1,1)) select p).ClassifyAs("Online");
Which is a one liner! with no fancy frameworks or type hierarchies required, just some useful extension methods.
Linq is generally well designed, well implemented, ubiquitous and well integrated into c#. Reimplementing the query parts of it would be foolish and wasteful, what you want is to add side effect causing operations to it. This is fine (you have mutable objects so this is hardly causing a problem) just add those operations. Just making them continue to yield their input will make your code fluent in style.
I had a think; Its only pseudo code but does this help?
public interface IPerson {
string PersonClassification { get; set; }
DateTime CreateDate { get; set; }
}
public class Person : IPerson {
public string PersonClassification { get; set; }
public DateTime CreateDate { get; set; }
}
public class TransformerBase<T>
where T : IPerson {
T Person { get; set; }
T Transform(Func<T, PersonClassification> transformer) {
return transformer(person);
}
}
public class OfflinePersonToOnlineTransformation : TransformerBase<Person>
{
public OfflinePersonToOnlineTransformation()
{
Transform(x => x.PersonClassification)
.WhenCreatedBefore("1/1/2000")
.ClassifyAs("Online");
}
}
public static class Extensions {
public static T WhenCreatedBefore<T>(this T person, string date) where T : IPerson{
if(person == null || person.CreateDate > DateTime.Parse(date))
return null
return person;
}
public static T Classify<T>(this T person, string classification)where T : IPerson{
if(person != null)
person.PersonClassification = classification;
return person;
}
}
It might help to take a step back and write a simple fluent interface first. You don't need generics or multiple classes to implement one. The main benefit of the fluent interface pattern is easy to read code. It's accomplished by returning this from methods to promote method chaining. Here's a basic example. I would start here and work backward to your desired result.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
class Calculator
{
List<int> values = new List<int>();
public Calculator Add(int value)
{
values.Add(value);
return this;
}
public int Count()
{
return values.Count;
}
public int Sum()
{
return values.Sum();
}
}
private void Form1_Load(object sender, EventArgs e)
{
//returns 3
int sum =
new Calculator()
.Add(1)
.Add(2)
.Sum();
}
}
Related
I'm creating an app that returns a list of metals to use in various conditions. I have a Metal class then classes for each type of Metal like Steel, Aluminum, etc. If I have a list of different Steels, I want to first select the best ones based on a set of properties common to all Metals then do a second pass based on the unique properties of Steel. (This isn't my exact problem but my problem is analogous.)
I can't figure out how to pass a List<Steel> to the GetBest() method of the Metal class as shown below that takes its first argument of type List<Metal>. The code won't compile due to error at the line highlighted below with **: "Argument 1: cannot convert from 'System.Collections.Generic.List<Steel>' to 'System.Collections.Generic.List<Metal>'.
public class Metal {
public int PropA { get; set; }
public List<Metal> GetBest( List<Metal> list, int condition1 )
{
var best = new List<Metal>();
//Analysis code here...
return best;
}
}
public class Steel : Metal
{
public int PropB { get; set; }
public List<Steel> GetBest(List<Steel> list, int condition1, int condition2 ) {
var bestSteel = new List<Steel>();
//Do first pass selection based on properties of all metals.
**bestSteel = Metal.GetBest(list, condition1);**
//Do some additional analysis based to Steel's unique properties.
//Analysis code here...
return bestSteel;
}
You could use a constrained generic method:
public static List<T> GetBest<T>(List<T> list, int condition1) where T : Metal
{
var best = new List<T>();
// Analysis code here...
return best;
}
I'm going to answer a different question! A look at how I might approach this problem without confounding my objects (Metal, Steel) with my logic for picking the best metal based on some conditions:
public class Metal{}
public class Steel:Metal{}
public class MetalPickerContext
{
public int Condition1{ get;set;}
}
public class MetalPicker<TMetal, TContext>
where TMetal: Metal
where TContext: MetalPickerContext
{
public virtual IEnumerable<TMetal> GetBest(IEnumerable<TMetal> list, TContext context)
{
var result = new List<TMetal>();
// logic for picking the best metal based on Condition1
return result;
}
}
public class SteelPickerContext: MetalPickerContext
{
public int Condition2{get;set;}
}
public class SteelPicker : MetalPicker<Steel,SteelPickerContext>
{
public override IEnumerable<Steel> GetBest(IEnumerable<Steel> list, SteelPickerContext context)
{
var initialResult = base.GetBest(list,context);
// Having called the base logic apply more with reference to Condition2
return initialResult;
}
}
This compiles (as you can see here) and I could expand the example a bit given some more details to make it a working one. Let me know if that would help you.
I am looking at some legacy code and have come across an abstraction that has properties for each of its derived/concrete types. I cannot share the exact code but please imagine that instead of it being a simple operation that there are numerous operations that are much more complex.
I have not come across anything like this before and have a lot of questions? First, is this a pattern that I am not aware of? If so, what is it? Second question, how should I refactor this so that it follows solid principles?
I will try my best to come up with a better example if needed.
public enum ToolType
{
Unknown = 0,
HRMonitor,
Dumbell,
SomeForceDevice
}
public abstract class ToolData
{
private ToolData()
{
IsValid = false;
this.ToolType = ToolType.Unknown;
}
public ToolData(ToolType toolType)
{
this.ToolType = toolType;
}
public ToolType ToolType { get; }
public virtual bool IsValid { get; protected set; } = true;
public double LinkQuality { get; set; }
public NullToolDataValue NullData => this as NullToolDataValue;
public DumbellDataValue DumbellData => this as DumbellDataValue;
public HeartRateDataValue HRData => this as HeartRateDataValue;
public SomeForceDataValue SomeForceData => this as SomeForceDataValue;
}
public class NullToolDataValue : ToolData
{
public NullToolDataValue() : base(ToolType.Unknown)
{
IsValid = false;
}
}
public class DumbellDataValue : ToolData
{
public double WeightValue { get; private set; }
public DumbellDataValue(double weightValue) : base(ToolType.Dumbell)
{
this.WeightValue = weightValue;
}
public override string ToString()
{
return WeightValue.ToString(CultureInfo.InvariantCulture);
}
}
public class HeartRateDataValue : ToolData
{
public int HeartRate { get; private set; }
public HeartRateDataValue(int heartRate) : base(ToolType.HRMonitor)
{
this.HeartRate = heartRate;
}
public override string ToString()
{
return HeartRate.ToString(CultureInfo.InvariantCulture);
}
}
public class SomeForceDataValue : ToolData
{
public double LeftHandForceValue { get; private set; }
public double RightHandForceValue { get; private set; }
public int LeftHandPosition { get; private set; }
public int RightHandPosition { get; private set; }
public SomeForceDataValue(double lefthandValue, double rightHandValue, int leftHandPosition, int rightHandPosition) : base(ToolType.SomeForceDevice)
{
this.LeftHandForceValue = lefthandValue;
this.LeftHandPosition = leftHandPosition;
this.RightHandForceValue = rightHandValue;
this.RightHandPosition = rightHandPosition;
}
public override string ToString()
{
return $"{LeftHandForceValue.ToString(CultureInfo.InvariantCulture)}" +
$"| {LeftHandPosition.ToString(CultureInfo.InvariantCulture)}" +
$"| {RightHandForceValue.ToString(CultureInfo.InvariantCulture)}" +
$"| {RightHandPosition.ToString(CultureInfo.InvariantCulture)}";
}
}
It is being used/consumed via something like the below which it too is missing some inheritance and things for brevity:
public class DumbellExcercise
{
public void ToolDataReceived(ToolData data)
{
if (data?.DumbellData == null) return;
//add value to some collection
Collection.Add(data.DumbellData.WeightValue);
}
}
public class HRExcercise
{
public void ToolDataReceived(ToolData data)
{
if (data?.HRData == null) return;
//add value to some collection
Collection.Add(data.HRData.HeartRate);
}
}
Okay, I'm going to give a shot at answering - hopefully this will help.
First up, ToolData shouldn't contain any References/Enums/whatever that list its subtypes. So first on the chopping block: all the lambda properties that cast the object as a specific subtype. I can kinda understand the appeal - you know an instance of ToolType happens to be a FloobieTool, so you call instance.FloobieTool and magically get a FloobieTool cast. But... well, there are problems that come with it, not the least is that you're breaking Opened/Closed Principle. Nothing wrong with making the person calling the class cast it explicitly with (FloobieTool)instance if they know they're working with a FloobieTool.
Next up: the ToolType. Why do you need this? You can tell if your instance of ToolData is a FloobieTool by simply doing an 'is' check in an IF condition:
void SomeFunc(ToolData toolData)
{
if (!(toolData is FloobieTool)) throw new Exception("Non-Floobie!");
// more code
}
I mean, what does that enumeration actually get you? Because it has a definite cost: it has to be kept in-sync with the list of classes that implement ToolData.
Also, the part in ToolDataReceived() for each of those Exercise classes seems... weird. I mean, you've got an exercise, and you're passing in ToolData. Why are you storing the amount of the Dumbell exercise? As opposed to just storing the ToolData. I mean, you're going through quite a bit of testing/casting/etc, just to add the dumbbell weight to a Collection. Any reason you can't just store the ToolData instance and call it a day? If you really need to specifically store Dumbbell info, you could do something like:
public class DumbbellExercise
{
List<DumbbellDataValue> dumbbellData = new List<DumbbellDataValue>();
public void AddToolData(ToolData toolData)
{
if (toolData is DumbbellDataValue)
this.dumbbellData.Add((DumbbellDataValue)toolData);
}
}
Hopefully that helps - it's tough to go into too many details when we're working off an abstracted example of your actual problem :-)
Having seen your edits, I believe even more firmly that the way to refactor this code is to use pattern matching. Pattern matching requires at least C# 7.0 so I'll include an almost-as-good way to do it pre-7.0 versions.
Step 1
Mark the properties obsolete using ObsoleteAttribute and pass true for the error parameter.
[Obsolete("Use pattern matching instead.", true)]
public NullToolDataValue NullData => this as NullToolDataValue;
[Obsolete("Use pattern matching instead.", true)]
public DumbellDataValue DumbellData => this as DumbellDataValue;
[Obsolete("Use pattern matching instead.", true)]
public HeartRateDataValue HRData => this as HeartRateDataValue;
[Obsolete("Use pattern matching instead.", true)]
public SomeForceDataValue SomeForceData => this as SomeForceDataValue;
This will make it a compiler error to use them in any code processed by the compiler. If you're doing any reflection on them, you'll get a runtime exception instead (after step 3 is complete) if you don't also change that code.
Step 2
Modify every call site that uses those properties to use pattern matching instead. If all you're doing is what you showed in the question, is should be as simple as this:
public class DumbellExcercise
{
public void ToolDataReceived(ToolData data)
{
if (data is DumbellDataValue dumbell)
Collection.Add(dumbell.WeightValue);
// OR
if (!(data is DumbellDataValue dumbell))
return;
Collection.Add(dumbell.WeightValue);
}
}
The second variation is not as pretty because the condition has to be parenthesized before it can be negated (hey, at least VB has the IsNot keyword; go figure) but you get the same early return that the existing code has.
It looks like you're using at least C# 6.0 because you're using the null-coalescing operator (?.), but if you're not using at least 7.0, you can do this, instead:
public class DumbellExcercise
{
public void ToolDataReceived(ToolData data)
{
DumbellDataValue dumbell = data as DumbellDataValue;
if (dumbell != null)
Collection.Add(dumbell.WeightValue);
// OR
DumbellDataValue dumbell = data as DumbellDataValue;
if (dumbell == null)
return;
Collection.Add(dumbell.WeightValue);
}
}
Step 3
Remove the properties. If there are no more compiler errors, the properties aren't being used, so you're free to get rid of them.
Additional Note
The IsValid property has a strange duality to it. It can be assigned by the derived classes but it's also virtual so it can be overridden, too. You really should pick one. If it were my decision, I'd keep it virtual and make it read-only.
public abstract class ToolData
{
// Continue to assume it's true...
public virtual bool IsValid => true;
}
public class NullToolDataValue : ToolData
{
// ...and indicate otherwise as needed.
public override bool IsValid => false;
}
I am trying to learn C# by making a simple program that shows the user sushi rolls given their desired ingredients. i.e. a user wants a roll with crab, and the program will spit out a list of sushi rolls that contain crab.
I've created a Roll class
public class Roll
{
private string name;
private List<string> ingredients = new List<string>();
}
With some getters and setters and other various methods.
In the GUI, I have some checkboxes which each call an update() method from the Control class, which will then need to check a list of rolls against a list of ingredients given by the GUI checkboxes. What I have is this
class Controller
{
static List<Roll> Rolls = new List<Roll>();
static RollList RL = new RollList();
static List<String> ingredients = new List<String>();
static Roll roll = new Roll();
}
public void update
{
foreach(Roll roll in Rolls)
{
foreach (String ingredient in ingredients)
if (!roll.checkForIngredient(ingredient))
Rolls.Remove(roll);
}
}
But a System.InvalidOperationException is thrown saying that because the collection was modified, the operation can't execute. OK, that's fair, but then what's the best way to do this? Here on Stack Overflow there's a post about removing elements from a generic list while iterating over it.
This was good and pointed me in the right direction, but unfortunately, my predicate condition simply doesn't match the top answer's.
It would have to iterate over the ingredients list, and I'm not even sure that's possible...
list.RemoveAll(roll => !roll.containsIngredient(each string ingredient in ingredients) );
shudder
I've tried the for loop, but I can't seem to get the enumeration to work either, and I wonder if it's even necessary to enumerate the class for just this method.
So I come here to try and find an elegant, professional solution to my problem. Keep in mind that I'm new to C# and I'm not all too familiar with predicate logic or enumeration on classes.
To use RemoveAll you can rewrite your condition to this:
list.RemoveAll(roll => !ingredients.All(roll.checkForIngredient));
This exploits the fact that when the compiler sees this, it will effectively rewrite it to this:
list.RemoveAll(roll => !ingredients.All(i => roll.checkForIngredient(i)));
Which is what you want. If not all the ingredients are present, remove the roll.
Now, having said that, since you say you're a beginner, perhaps you feel more comfortable keeping your loop, if you could just make it work (ie. stop crashing due to modifying the loop). To do that, just make a copy of the collection and then loop through the copy, you can do this by just modifying the foreach statement to this:
foreach(Roll roll in Rolls.ToList())
This will create a list based copy of the Rolls collection, and then loop on that. The list will not be modified, even if Rolls is, it is a separate copy containing all the elements of Rolls when it was created.
As requested in the comments, I'll try to explain how this line of code works:
list.RemoveAll(roll => !ingredients.All(roll.checkForIngredient));
The RemoveAll method, which you can see the documentation for here takes a predicate, a Predicate<T>, which is basically a delegate, a reference to a method.
This can be a lambda, syntax that creates an anonymous method, using the => operator. An anonymous method is basically a method declared where you want to use it, without a name, hence the anonymous part. Let's rewrite the code to use an anonymous method instead of a lambda:
list.RemoveAll(delegate(Roll roll)
{
return !ingredients.All(roll.checkForIngredient);
});
This is the exact same compiled code as for the lambda version above, just using the bit more verbose syntax of an anonymous method.
So, how does the code inside the method work.
The All method is an extension method, found on the Enumerable class: Enumerable.All.
It will basically loop through all the elements of the collection it is extending, in this case the ingredients collection of a single roll, and call the predicate function. If for any of the elements the predicate returns false, the result of calling All will also be false. If all the calls return true, the result will also be true. Note that if the collection (ingredients) is empty, the result will also be true.
So let's try to rewrite our lambda code, which again looked like this:
list.RemoveAll(roll => !ingredients.All(roll.checkForIngredient));
Into a more verbose method, not using the All extension method:
list.RemoveAll(delegate(Roll roll)
{
bool all = true;
foreach (var ingredient in ingredients)
if (!roll.checkForIngredient(ingredient))
{
all = false;
break;
}
return !all;
});
This now starts to look like your original piece of code, except that we're using the RemoveAll method, which needs a predicate that returns whether to remove the item or not. Since if all is false, we need to remove the roll, we use the not operator ! to reverse that value.
Since you are both new to C# but also asked for an elegant solution, I will give you an example of how to solve this using a more object-oriented approach.
First of all, any "thing" of significance should be modeled as a class, even if it has just one property. This makes it easier to extend the behavior later on. You already defined a class for Roll. I would also add a class for Ingredient:
public class Ingredient
{
private string _name;
public string Name
{
get { return _name; }
}
public Ingredient(string name)
{
_name = name;
}
}
Note the Name property which only has a getter, and the constructor which accepts a string name. This might look like unnecessary complexity at first but will make your code more straightforward to consume further down the road.
Next, we'll modify your Roll class according to this guideline and give it some helper methods that make it easier for us to check if a roll contains a certain (list of) ingredients:
public class Roll
{
private string _name;
private List<Ingredient> _ingredients = new List<Ingredient>();
public string Name
{
// By only exposing the property through a getter, you are preventing the name
// from being changed after the roll has been created
get { return _name; }
}
public List<Ingredient> Ingredients
{
// Similarly here, you are forcing the consumer to use the AddIngredient method
// where you can do any necessary checks before actually adding the ingredient
get { return _ingredients; }
}
public Roll(string name)
{
_name = name;
}
public bool AddIngredient(Ingredient ingredient)
{
// Returning a boolean value to indicate whether the ingredient was already present,
// gives the consumer of this class a way to present feedback to the end user
bool alreadyHasIngredient = _ingredients.Any(i => i.Name == ingredient.Name);
if (!alreadyHasIngredient)
{
_ingredients.Add(ingredient);
return true;
}
return false;
}
public bool ContainsIngredients(IEnumerable<Ingredient> ingredients)
{
// We use a method group to check for all of the supplied ingredients
// whether or not they exist
return ingredients.All(ContainsIngredient);
// Could be rewritten as: ingredients.All(i => ContainsIngredient(i));
}
public bool ContainsIngredient(Ingredient ingredient)
{
// We simply check if an ingredient is present by comparing their names
return _ingredients.Any(i => i.Name == ingredient.Name);
}
}
Pay attention to the ContainsIngredient and ContainsIngredients methods here. Now you can do stuff like if (roll.ContainsIngredient(ingredient)), which will make your code more expressive and more readable. You'll see this in action in the next class that I'm going to add, RollCollection.
You are modeling collections of food to pick from, presumably in the context of a restaurant menu or some similar domain. You might as well go ahead and model just that: a RollCollection. This will allow you to encapsulate some meaningful logic inside of the collection.
Again, this sort of thing tends to require some boilerplate code and may look overly complex at first, but it will make your classes easier to consume. So let's add a RollCollection:
public class RollCollection : IEnumerable<Roll>
{
private List<Roll> _rolls = new List<Roll>();
public RollCollection()
{
// We need to provide a default constructor if we want to be able
// to instantiate an empty RollCollection and then add rolls later on
}
public RollCollection(IEnumerable<Roll> rolls)
{
// By providing a constructor overload which accepts an IEnumerable<Roll>,
// we have the opportunity to create a new RollCollection based on a filtered existing collection of rolls
_rolls = rolls.ToList();
}
public RollCollection WhichContainIngredients(IEnumerable<Ingredient> ingredients)
{
IEnumerable<Roll> filteredRolls = _rolls
.Where(r => r.ContainsIngredients(ingredients));
return new RollCollection(filteredRolls);
}
public bool AddRoll(Roll roll)
{
// Similar to AddIngredient
bool alreadyContainsRoll = _rolls.Any(r => r.Name == roll.Name);
if (!alreadyContainsRoll)
{
_rolls.Add(roll);
return true;
}
return false;
}
#region IEnumerable implementation
public IEnumerator<Roll> GetEnumerator()
{
foreach (Roll roll in _rolls)
{
yield return roll;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
}
WhichContainIngredients is the thing we were really looking for, as it allows you to do something like this:
// I have omitted the (proper) instantiation of Rolls and ChosenIngredients for brevity here
public RollCollection Rolls { get; set; }
public List<Ingredient> ChosenIngredients { get; set; }
public void Update()
{
Rolls = Rolls.WhichContainIngredients(ChosenIngredients);
}
This is simple and clean, just the sort of thing you want to be doing in your presentation layer. The logic to accomplish your requirement is now nicely encapsulated in the RollCollection class.
EDIT: a more complete (but still simplified) example of how your Controller class might end up looking like:
public class Controller
{
private RollCollection _availableRolls = new RollCollection();
private List<Ingredient> _availableIngredients = new List<Ingredient>();
public RollCollection AvailableRolls
{
get { return _availableRolls; }
}
public List<Ingredient> AvailableIngredients
{
get { return _availableIngredients; }
}
public RollCollection RollsFilteredByIngredients
{
get { return AvailableRolls.WhichContainIngredients(ChosenIngredients); }
}
public List<Ingredient> ChosenIngredients { get; set; }
public Controller()
{
ChosenIngredients = new List<Ingredient>();
InitializeTestData();
}
private void InitializeTestData()
{
Ingredient ingredient1 = new Ingredient("Ingredient1");
Ingredient ingredient2 = new Ingredient("Ingredient2");
Ingredient ingredient3 = new Ingredient("Ingredient3");
_availableIngredients.Add(ingredient1);
_availableIngredients.Add(ingredient2);
_availableIngredients.Add(ingredient3);
Roll roll1 = new Roll("Roll1");
roll1.AddIngredient(ingredient1);
roll1.AddIngredient(ingredient2);
Roll roll2 = new Roll("Roll2");
roll2.AddIngredient(ingredient3);
_availableRolls.AddRoll(roll1);
_availableRolls.AddRoll(roll2);
}
}
I am trying to learn C# by making a simple program that shows the user
sushi rolls given their desired ingredients. i.e. a user wants a roll
with crab, and the program will spit out a list of sushi rolls that
contain crab.
Here's my solution to the given problem:
public class Roll
{
public string Name { get; set; }
private List<string> ingredients = new List<string>();
public IList<string> Ingredients { get { return ingredients; } }
public bool Contains(string ingredient)
{
return Ingredients.Any(i => i.Equals(ingredient));
}
}
You can use the LINQ extension method .Where to filter your collection of Rolls
public class Program
{
static void Main()
{
var allRolls = new List<Roll>
{
new Roll
{
Name = "Roll 1",
Ingredients = { "IngredientA", "Crab", "IngredientC" }
},
new Roll
{
Name = "Roll 2",
Ingredients = { "IngredientB", "IngredientC" }
},
new Roll
{
Name = "Roll 3",
Ingredients = { "Crab", "IngredientA" }
}
};
var rollsWithCrab = allRolls.Where(roll => roll.Contains("Crab"));
foreach (Roll roll in rollsWithCrab)
{
Console.WriteLine(roll.Name);
}
}
}
From what I see you're trying to remove all rolls that don't contain crab from your list of rolls. A better approach is to filter out those rolls that don't contain crab (using .Where), you can then use .ToList() if you need to manipulate the whole list directly rather than iterating through the collection (fetching one item at a time).
You should read up on Delegates, Iterators, Extension Methods and LINQ to better understand what's going on under the covers.
Don't be scared of the extensive code. The problem is general. I just provided the code to understand the problem better.
I am trying to find out a standard approach of manipulating tables with many-to-many relationships. And I am almost done. Here Teacher and Course have M:M relationship. I have designed my classes as follows:
Teacher - class:
public class Teacher
{
public int ID{get;set;}
public string TeacherName{get;set;}
private List<Course> _items = null;
public List<Course> Items
{
get
{ if (_items == null) {_items = Course.GetCoursesByTeacherID(_ID);}
return _items;
}
set {_items = value;}
}
public int Save()
{ //...
CourseTeacher.DeleteCoursesByTeacherID(tc, id);
CourseTeacher.SaveCoursesWithTeacherID(tc, id, this.Items);
//...
}
public bool Update()
{ //...
CourseTeacher.DeleteCoursesByTeacherID(tc, this.ID);
CourseTeacher.SaveCoursesWithTeacherID(tc, this.ID, this.Items);
//...
}
public static Teacher Get(int id)
{ //...
item.Items = CourseTeacher.GetCoursesByTeacherID(tc, item.ID);//...
}
public static List<Teacher> Get()
{ //...
items[i].Items = CourseTeacher.GetCoursesByTeacherID(tc, items[i].ID);//...
}
public static List<Teacher> GetTeachersByCourseID(int id)
{ //...
items = CourseTeacher.GetTeachersByCourseID(tc, id);//...
}
public bool Delete()
{ //...
CourseTeacher.DeleteCoursesByTeacherID(tc, this.ID);//...
}
}
Course is absolutely similar to Teacher - class. And the mapping class is as follows:
public class CourseTeacher
{
public int CourseID{get;set;}
public int TeacherID{get;set;}
public static void SaveCoursesWithTeacherID(TransactionContext tc, int teacherID, List<Course> items){}
public static void SaveTeachersWithCourseID(TransactionContext tc, int courseID, List<Teacher> items){}
private void Save(TransactionContext tc){}
public static void DeleteCoursesByTeacherID(TransactionContext tc, int teacherID){}
public static void DeleteTeachersByCourseID(TransactionContext tc, int courseID){}
public static List<Teacher> GetTeachersByCourseID(TransactionContext tc, int courseID){}
public static List<Course> GetCoursesByTeacherID(TransactionContext tc, int teacherID){}
}
Now my problem is, this code is not working?
Teacher professorXyz = Teacher.Get(2);
Course cpp = Course.Get(3);
Course java = Course.Get(2);
professorXyz.Items.Remove(cpp);
professorXyz.Items.Remove(java);
professorXyz.Update();
This is not working because it is not probably finding a match or get accessor is returning readonly List.
How should I refactor my Teacher/Course - class to achieve this?
No exception. No problem with persistence code. Items are not being removed.
why professorXyz.Items.Contains(cpp); is returning false?
What to check for?
This is not a direct answer, but...
Your design is very (very) Relational. That makes persisting to a DB easier but you do not have a proper OO model. Maybe you should consider using DataTables in a DataSet and reap the benefits of the Relation class.
To take a shot:
Teacher professorXyz = Teacher.Get(2);
Course cpp = Course.Get(3);
I suspect that the cpp course is being loaded twice, and that there are 2 instances of that course in memory. A very bad consequence of your design. By default, those 2 instances will not be equal and that is why Remove does not work. You could overload Equals, == and GethashCode but that is not recommended for mutable types.
What you really need is a design where for a given Teacher or Course there never exists more than 1 instance in memory.
Re Comment: A MxM relation in OO looks like:
class Teacher
{
public readonly List<Course> Courses = ...;
}
class Course
{
public readonly List<Teacher> Teachers = ...;
}
This will take a little more work to write to a DB but it solves a lot of other problems.
What are you trying to do? Your sample looks like you want to build a relational database table implemented in C#.
If you want to have an OO representation then get rid of the entire CourseTeacher class. That has absolutely nothing to do with OO.
seems you already solved this problem, but consider following code where I overrode bool Equals; C# couldn't knew how to compare your new cpp instance with another instance in your List<Course>, so we need to tell it by creating a more specialized Equals method:
class Teacher
{
private List<Course> items = new List<Course>();
public int ID { get; set; }
public List<Course> Items { get { return items; } }
}
class Course
{
public int ID { get; set; }
public override int GetHashCode() { return base.GetHashCode(); }
public override bool Equals(object obj) { return Equals(obj as Course); }
public bool Equals(Course another)
{
return another != null && this.ID.Equals(another.ID);
}
}
static void Main(string[] args)
{
Teacher teacher = new Teacher { ID = 2 };
teacher.Items.AddRange(
new Course[] {
new Course{ ID = 2 }, // java
new Course{ ID = 3 } }); // cpp
Course cpp = new Course { ID = 3 }; // previous problem: another instance
teacher.Items.Contains(cpp); // now returns true
teacher.Items.Remove(cpp); // now returns true
}
Henk is correct; your design is very, very relational. For this sort of scenario, though, you're better off focusing on behaviour in your objects, and using an object-relational mapping (ORM) tool to translate between your objects and your database.
ADO.NET's DataTable and DataSet don't really offer object-relational mapping capabilities; because they're so tightly coupled to the underlying database schema, they force you to think in terms of columns, tables and relations, when you really want to be thinking in terms of teachers and courses.
I would seriously recommend looking at Castle ActiveRecord for this scenario. It uses the same approach as your example - static Teacher.Get() to retrieve an instance, myTeacher.Save() to save your changes - but there's a LOT of necessary complexity that your example is missing, and using an ORM framework will allow you to ignore this complexity and focus on your own project's requirements.
Here's an example of many-many associations from the Castle ActiveRecord documentation that you may find helpful.
How about adding and removing done within the Teacher Class?
public class Teacher
{
//.... Original implementations
public bool AddCourse(Course course) {
if(_items.Contains(course)) return false;
_items.Add(course);
return true;
}
// similarly for remove course
}
As a result of another question I asked here I want to use a HashSet for my objects
I will create objects containing a string and a reference to its owner.
public class Synonym
{
private string name;
private Stock owner;
public Stock(string NameSynonym, Stock stock)
{
name=NameSynonym;
owner=stock
}
// [+ 'get' for 'name' and 'owner']
}
I understand I need a comparer , but never used it before. Should I create a separate class? like:
public class SynonymComparer : IComparer<Synonym>
{
public int Compare(Synonym One, Synonym Two)
{ // Should I test if 'One == null' or 'Two == null' ????
return String.Compare(One.Name, Two.Name, true); // Caseinsesitive
}
}
I prefer to have a function (or nested class [maybe a singleton?] if required) being PART of class Synonym instead of another (independent) class. Is this possible?
About usage:
As i never used this kind of thing before I suppose I must write a Find(string NameSynonym) function inside class Synonym, but how should I do that?
public class SynonymManager
{
private HashSet<SynonymComparer<Synonym>> ListOfSynonyms;
public SynonymManager()
{
ListOfSymnonyms = new HashSet<SynonymComparer<Synonym>>();
}
public void SomeFunction()
{ // Just a function to add 2 sysnonyms to 1 stock
Stock stock = GetStock("General Motors");
Synonym otherName = new Synonym("GM", stock);
ListOfSynonyms.Add(otherName);
Synonym otherName = new Synonym("Gen. Motors", stock);
ListOfSynonyms.Add(otherName);
}
public Synonym Find(string NameSynomym)
{
return ListOfSynonyms.??????(NameSynonym);
}
}
In the code above I don't know how to implement the 'Find' method. How should i do that?
Any help will be appreciated
(PS If my ideas about how it should be implemented are completely wrong let me know and tell me how to implement)
A HashSet doesn't need a IComparer<T> - it needs an IEqualityComparer<T>, such as
public class SynonymComparer : IEqualityComparer<Synonym>
{
public bool Equals(Synonym one, Synonym two)
{
// Adjust according to requirements.
return StringComparer.InvariantCultureIgnoreCase
.Equals(one.Name, two.Name);
}
public int GetHashCode(Synonym item)
{
return StringComparer.InvariantCultureIgnoreCase
.GetHashCode(item.Name);
}
}
However, your current code only compiles because you're creating a set of comparers rather than a set of synonyms.
Furthermore, I don't think you really want a set at all. It seems to me that you want a dictionary or a lookup so that you can find the synonyms for a given name:
public class SynonymManager
{
private readonly IDictionary<string, Synonym> synonyms = new
Dictionary<string, Synonym>();
private void Add(Synonym synonym)
{
// This will overwrite any existing synonym with the same name.
synonyms[synonym.Name] = synonym;
}
public void SomeFunction()
{
// Just a function to add 2 synonyms to 1 stock.
Stock stock = GetStock("General Motors");
Synonym otherName = new Synonym("GM", stock);
Add(otherName);
ListOfSynonyms.Add(otherName);
otherName = new Synonym("Gen. Motors", stock);
Add(otherName);
}
public Synonym Find(string nameSynonym)
{
// This will throw an exception if you don't have
// a synonym of the right name. Do you want that?
return synonyms[nameSynonym];
}
}
Note that there are some questions in the code above, about how you want it to behave in various cases. You need to work out exactly what you want it to do.
EDIT: If you want to be able to store multiple stocks for a single synonym, you effectively want a Lookup<string, Stock> - but that's immutable. You're probably best storing a Dictionary<string, List<Stock>>; a list of stocks for each string.
In terms of not throwing an error from Find, you should look at Dictionary.TryGetValue which doesn't throw an exception if the key isn't found (and also returns whether or not the key was found); the mapped value is "returned" in an out parameter.
Wouldn't it be more reasonable to scrap the Synonym class entirely and have list of synonyms to be a Dictonary (or, if there is such a thing, HashDictionary) of strings?
(I'm not very familiar with C# types, but I hope this conveys general idea)
The answer I recommend (edited, now respects the case):
IDictionary<string, Stock>> ListOfSynonyms = new Dictionary<string,Stock>>();
IDictionary<string, string>> ListOfSynForms = new Dictionary<string,string>>();
class Stock
{
...
Stock addSynonym(String syn)
{
ListOfSynForms[syn.ToUpper()] = syn;
return ListOfSynonyms[syn.ToUpper()] = this;
}
Array findSynonyms()
{
return ListOfSynonyms.findKeysFromValue(this).map(x => ListOfSynForms[x]);
}
}
...
GetStock("General Motors").addSynonym('GM').addSynonym('Gen. Motors');
...
try
{
... ListOfSynonyms[synonym].name ...
}
catch (OutOfBounds e)
{
...
}
...
// output everything that is synonymous to GM. This is mix of C# and Python
... GetStock('General Motors').findSynonyms()
// test if there is a synonym
if (input in ListOfSynonyms)
{
...
}
You can always use LINQ to do the lookup:
public Synonym Find(string NameSynomym)
{
return ListOfSynonyms.SingleOrDefault(x => x.Name == NameSynomym);
}
But, have you considered using a Dictionary instead, I believe it is better suited for extracting single members, and you can still guarantee that there are no duplicates based on the key you choose.
I am not sure that lookup time is of SingleOrDefault, but I am pretty sure it is linear (O(n)), so if lookup time is important to you, a Dictionary will provide you with O(1) lookup time.