Two classes but same object - c#

I have 2 tables, tblA and tblB, with same fields and types. I get datas through linq to sql so I have 2 partial classes clsTblA and clsTblB.
I have a combo to choose tblA or tblB and I have to read in that table and do some query.
What I'am trying to do is evitate to duplicate code to run the same methods.
So now I have (in pseudo-code):
if (combo == "A")
{
List<clsTblA> listUserNow = ctx.clsTblA.Where(p => p.blabla).ToList();
List<clsTblA> listUserLastYear = ctx.clsTblA.Where(q => q.blabla).ToList();
}
if (combo == "B")
{
List<clsTblB> listUserNow = ctx.clsTblB.Where(p => p.blabla).ToList();
List<clsTblB> listUserLastYear = ctx.clsTblB.Where(q => q.blabla).ToList();
}
But I have in mind something like this (in pseudo-code):
SupClsTable clsTblX = null;
if (combo == A)
clsTblX = new clsTblA();
if (combo == B)
clsTblX = new clsTblB();
List<clsTblX> listUserNow = tblX.QueryForNow();
List<clsTblX> listUserLastYear = tblX.QueryForLastYear();
Does it exist something like this?
I also searched in design pattern but without results.
Thanks in advance.
EDIT 1:
At this moment the code is like:
if (combo == A)
{
using (DbDataContext ctx = new DbDataContext())
{
List<clsTblA> listUserNow = ctx.clsTblA.Where(p => p.blabla).ToList();
List<clsTblA> listUserLastYear = ctx.clsTblA.Where(q => q.blabla).ToList();
}
}
if (combo == B)
{
using (DbDataContext ctx = new DbDataContext())
{
List<clsTblB> listUserNow = ctx.clsTblB.Where(p => p.blabla).ToList();
List<clsTblB> listUserLastYear = ctx.clsTblB.Where(q => q.blabla).ToList();
}
}
so I have twice listUserNow and listUserLastYear.
How can I let me return a unique
using (DbDataContext ctx = new DbDataContext())
{
List<*something*> listUserNow = ctx.*something*.Where(p => p.blabla).ToList();
List<*something*> listUserLastYear = ctx.*something*.Where(p => p.blabla).ToList();
}
indipendent from "if combo"?
Thanks in advence

It seems like what you're looking for are Interfaces. i.e.
interface ISampleInterface
{
void SampleMethod();
}
class ImplementationClass : ISampleInterface
{
// Explicit interface member implementation:
void ISampleInterface.SampleMethod()
{
// Method implementation.
}
}
You can learn more about that on the Microsoft documentation site Microsoft Docs
EDIT: To clearify. Once both Classes inerhit from the Interface you could write the following
if (combo == A)
clsTblX = new clsTblA();
if (combo == B)
clsTblX = new clsTblB();
as follows
IClsTable clsTbl = (combo == A)? new ClsTblA() : new ClsTblB() // example
and work with the clsTbl like you would've previously with ClsTblA or ClsTblB since both follow the same structure and have same properties and methods.
Maybe this dotnetfiddle example helps you understand the concept of this solution.

While personally I think interfacing is better option for your use case, you could also implement something like this that will make it easy for you to extend. Same can be casted against interface as well. So every time a new combo is added just add a case and it extends easily.
public class ComboFactory
{
public static SuperCombo GetComboClassInstance(string comboCode)
{
switch(comboCode)
{
case "A":
return new ComboA();
case "B":
return new ComboB();
//and so on
}
}
}

Try to see if this is what you need:
public interface iTbl
{
int Property1 { get; set; }
string Property2 { get; set; }
void WhatAreYou();
}
public class clsTblA : iTbl
{
public int Property1 { get; set; }
public string Property2 { get; set; }
public void WhatAreYou()
{
Console.WriteLine("I am a clsTblA!");
}
}
public class clsTblB : iTbl
{
public int Property1 { get; set; }
public string Property2 { get; set; }
public void WhatAreYou()
{
Console.WriteLine("I am a clsTblB!");
}
}
public class Program
{
public static void Main()
{
List<iTbl> tbls = new List<iTbl>()
{
new clsTblA(),
new clsTblB(),
new clsTblB(),
new clsTblA(),
new clsTblA()
};
foreach (var tbl in tbls)
{
tbl.WhatAreYou();
}
}
}
Output:
I am a clsTblA!
I am a clsTblB!
I am a clsTblB!
I am a clsTblA!
I am a clsTblA!

Related

How to struct an object to represent a list of topics?

I'm coding at C# and I'm trying to make an OOP representation of a list of topics. I tried countless approaches but I still not being able to reach the desired result.
I want to make a method later that will output it like:
1) Text
1.1) Text
2) Text
2.1) Text
2.2) Text
2.2.1) Text
2.2.2) Text
2.3) Text
3) Text
3.1) Text
When needed to get a single topic, I would like to create a method calling my object like:
private string GetSingleTopic()
{
return $"{Topic.Numerator}) {Topic.Text}"
}
EXAMPLES
Example 1
I would be able to instantiate the object such as:
var variable = new TopicObject
{
"TitleA",
"TitleB",
"TitleC"
}
/* --- OUTPUT ---
1) TitleA
2) TitleB
3) TitleC
--- OUTPUT --- */
Example 2
Be able to instantiate the object such as:
var variable = new TopicObject
{
"TitleA",
"TitleB",
"TitleC":
{
"TitleD":
{
"TitleE"
},
"TitleF":
{
"TitleG",
"TitleH"
}
}
}
/* --- OUTPUT ---
1) TitleA
2) TitleB
3) TitleC
3.1) TitleD
3.1.2) TitleE
3.2) TitleF
3.2.1) TitleG
3.2.2) TitleH
--- OUTPUT --- */
My Approach
This, was one of my many approaches. I couldn't use it because I can't initialize the inner topic List in the way i mentioned, like an hierarchy.
But the structure is pretty similar to what I want to achieve so I decided to put here as an example.
public abstract class TopicBase
{
public List<Topic> Topics { get; set; } // optional
protected TopicBase() { Topics = new List<Topic>(); }
protected TopicBase(List<Topic> topics) { Topics = topics; }
public TopicBase AddTopic(string topicText)
{
var test = new Topic(topicText);
Topics.Add(test);
return this;
}
}
public class Topic
{
public Topic(string text)
{
Numerator++;
Text = text;
}
public int Numerator { get; }
public string Text { get; }
}
public class TopicLevel1 : TopicBase { }
public class TopicLevel2 : TopicBase { }
public class TopicLevel3 : TopicBase { }
Let's start by defining a data structure that can hold the topics:
public class Topics<T> : List<Topic<T>> { }
public class Topic<T> : List<Topic<T>>
{
public T Value { get; private set; }
public Topic(T value, params Topic<T>[] children)
{
this.Value = value;
if (children != null)
this.AddRange(children);
}
}
That allows us to write this code:
var topics = new Topics<string>()
{
new Topic<string>("TitleA"),
new Topic<string>("TitleB"),
new Topic<string>("TitleC",
new Topic<string>("TitleD",
new Topic<string>("TitleE")),
new Topic<string>("TitleF",
new Topic<string>("TitleF"),
new Topic<string>("TitleH")))
};
That matches your "Example 2" data.
To output the result we add two ToOutput methods.
To Topics<T>:
public IEnumerable<string> ToOutput(Func<T, string> format)
=> this.SelectMany((t, n) => t.ToOutput(0, $"{n + 1}", format));
To Topic<T>:
public IEnumerable<string> ToOutput(int depth, string prefix, Func<T, string> format)
{
yield return $"{new string(' ', depth)}{prefix}) {format(this.Value)}";
foreach (var child in this.SelectMany((t, n) => t.ToOutput(depth + 1, $"{prefix}.{n + 1}", format)))
{
yield return child;
}
}
Now I can run this code:
foreach (var line in topics.ToOutput(x => x))
{
Console.WriteLine(line);
}
That gives me:
1) TitleA
2) TitleB
3) TitleC
3.1) TitleD
3.1.1) TitleE
3.2) TitleF
3.2.1) TitleF
3.2.2) TitleH
If the goal is to have some structure that will help with the output of the topic hierarchy, you already have it (and may even be able to simplify it more).
For example, here's an almost-minimal Topic to get what you want:
public class Topic
{
public string Title { get; set; }
public List<Topic> SubTopics { get; private set; } = new();
public Topic() : this("DocRoot") { }
public Topic(string title) => Title = title;
public void AddTopics(List<Topic> subTopics) => SubTopics.AddRange(subTopics);
public void AddTopics(params Topic[] subTopics) => SubTopics.AddRange(subTopics);
public override string ToString() => Title;
}
That is, you have a Topic that can have SubTopics (aka children) and that's all you need.
With that, we can build your second example:
var firstLevelTopics = new List<Topic>();
for (var c = 'A'; c < 'D'; ++c)
{
firstLevelTopics.Add(new Topic(c.ToString()));
}
var cTopic = firstLevelTopics.Last();
cTopic.AddTopics(
new Topic
{
Title = "D",
SubTopics = { new Topic("E") }
},
new Topic
{
Title = "F",
SubTopics = { new Topic("G"), new Topic("H") }
});
Now, imagine if we had a function to print the hierarchy from the list of top-level topics. I'm leaving the final detail for yourself in case this is homework.
PrintTopics(firstLevelTopics);
static void PrintTopics(List<Topic> topics, string prefix = "")
{
// For the simple case, we can just loop and print...
for (var i = 0; i < topics.Count; ++i)
{
var topic = topics[i];
var level = i + 1;
Console.WriteLine($"{prefix}{level}) {topic}");
// ...but, if we want to print the children, we need more.
// Make a recursive call to print the SubTopics
// PrintTopics(<What goes here?>);
}
}

Replace object on heap?

Maybe this is real simple or breaking all the rules or maybe I just dont know what its called so I cant find it.
Anyway, I want to be able to replace an entire object on the heap. I've added a small code sample to show what I want to do, and a way of doing it, but I just want to know if there is a more elegant way?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BasicObjectTest
{
class Program
{
static void Main(string[] args)
{
List<Test> testList = new List<Test>
{
new Test {Value=1,NiceString="First" },
new Test {Value=2,NiceString="Second" },
new Test {Value=3,NiceString="Third" }
};
var replacementTestClass = new Test { Value = 2, NiceString = "NEW" };
EasyWay(testList, replacementTestClass);
var correctTestClass = testList.FirstOrDefault(x => x.Value == 2);
Console.WriteLine(correctTestClass.NiceString); //Expecting "Forth"
Console.ReadLine();
HardWay(testList, replacementTestClass);
correctTestClass = testList.FirstOrDefault(x => x.Value == 2);
Console.WriteLine(correctTestClass.NiceString);
Console.ReadLine();
}
private static void HardWay(List<Test> testList, Test replacementTestClass)
{
//This will work!
var secondTestClass = testList.FirstOrDefault(x => x.Value == 2);
CopyPropertiesUsingPropertyInfo(secondTestClass, replacementTestClass);
}
private static void CopyPropertiesUsingPropertyInfo(Test secondTestClass, Test replacementTestClass)
{
foreach(var pi in secondTestClass.GetType().GetProperties())
{
pi.SetValue(secondTestClass, pi.GetValue(replacementTestClass, null));
}
}
private static void EasyWay(List<Test> testList, Test replacementTestClass)
{
//This wont work, but I want it to!
var secondTestClass = testList.FirstOrDefault(x => x.Value == 2);
secondTestClass = replacementTestClass;
}
}
}
and my Test object
class Test
{
public int Value { get; set; }
public string NiceString { get; set; }
}
There must be a more elegant way of doing this?
I know why the first alternative does not work: I just change the object reference for that variable.
Update:
Using this thinking I understood it for a long time I tested this now thinking it would work, but the test fails. Why? Didnt I replace the object so that every object using it should use the new object? See complete code below
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
var main = new Main { Property = 1 };
var dependent = new Dependent(main);
void ChangeRef(ref Main Oldmain, Main newMain)
{
Oldmain = newMain;
}
ChangeRef(ref main, new Main { Property = 5 });
Assert.AreEqual(5,dependent.Main.Property);
}
}
public class Main
{
public int Property { get; set; }
}
public class Dependent
{
public Dependent(Main main)
{
Main = main;
}
public Main Main { get; set; }
}
There must be a more elegant way of doing this?
There is one basic thing you're missing. When you search for the object in the list, and one is found, you get back a copy of the reference pointing to that object. This means that when you alter it, you're only altering the copy. The original reference in the list is still pointing to that same old object instance.
but what if I didnt have a list. I just had the object reference in a
variable?
Then you could use the ref keyword to pass the reference type by reference:
public static void Main(string[] args)
{
var test = new Test { Value = 1, NiceString = "First" };
var newTest = new Test { Value = 2, NiceString = "AlteredTest!" };
UpdateTest(ref test, newTest);
Console.WriteLine(test.NiceString); // "AlteredTest!"
}
public static void UpdateTest(ref Test originalTest, Test other)
{
originalTest = other;
}
An alternative way to approach this is with the proverbial "extra level of indirection".
Instead of storing the objects in the list, you store wrapper objects instead. The wrapper object provides an "Item" field which points to the actual object. Then you can update the "Item" field to point it at the new object.
A simple generic wrapper class could look like this:
class Wrapper<T>
{
public T Item;
public Wrapper(T item)
{
Item = item;
}
public static implicit operator Wrapper<T>(T item)
{
return new Wrapper<T>(item);
}
}
Then you could use it like so:
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApplication2
{
class Test
{
public int Value { get; set; }
public string NiceString { get; set; }
}
class Wrapper<T>
{
public T Item;
public Wrapper(T item)
{
Item = item;
}
public static implicit operator Wrapper<T>(T item)
{
return new Wrapper<T>(item);
}
}
class Program
{
static void Main(string[] args)
{
var testList = new List<Wrapper<Test>>
{
new Test {Value = 1, NiceString = "First"},
new Test {Value = 2, NiceString = "Second"},
new Test {Value = 3, NiceString = "Third"}
};
var replacementTestClass = new Test { Value = 2, NiceString = "NEW" };
EasyWay(testList, replacementTestClass);
var correctTestClass = testList.FirstOrDefault(x => x.Item.Value == 2);
Console.WriteLine(correctTestClass.Item.NiceString); //Expecting "New"
Console.ReadLine();
}
private static void EasyWay(List<Wrapper<Test>> testList, Test replacementTestClass)
{
var secondTestClass = testList.FirstOrDefault(x => x.Item.Value == 2);
secondTestClass.Item = replacementTestClass;
}
}
}

List.except on custom class

lets say I have a custom class:
public class WineCellar
{
public string year;
public string wine;
public double nrbottles;
}
Lets say I now have a List of this custom class:
List<WineCellar> orignialwinecellar = List<WineCellar>();
containing these items:
2012 Chianti 12
2011 Chianti 6
2012 Chardonay 12
2011 Chardonay 6
I know that if I want to compare two list and return a new list that has only items that are not in the other list I would do:
var newlist = list1.Except(list2);
How can I extend this to a custom class? Lets say I have:
string[] exceptionwinelist = {"Chardonay", "Riesling"};
I would like this to be returned:
List<WineCellar> result = originalwinecellar.wine.Except(exceptionwinelist);
This pseudocode obviously doesnt work but hopefully illustrates what I m trying to do. This shoudl then return a List of the custom class winecellar with following items:
2012 Chianti 12
2011 Chianti 6
Thanks.
You don't really want to use Except here, as you don't have a collection of WineCellar objects to use as a blacklist. What you have is a collection of rules: "I don't want objects with such and such wine names".
Therefore it's better to simply use Where:
List<WineCellar> result = originalwinecellar
.Where(w => !exceptionwinelist.Contains(w.wine))
.ToList();
In human-readable form:
I want all WineCellars where the wine name is not present in the list of exceptions.
As an aside, the WineCellar class name is a bit misleading; those objects are not cellars, they are inventory items.
One solution is with an extension method:
public static class WineCellarExtensions
{
public static IEnumerable<WineCellar> Except(this List<WineCellar> cellar, IEnumerable<string> wines)
{
foreach (var wineCellar in cellar)
{
if (!wines.Contains(wineCellar.wine))
{
yield return wineCellar;
}
}
}
}
And then use it like this:
List<WineCellar> result = originalwinecellar.Except(exceptionwinelist).ToList();
exceptionWineList is a string[] but originalWineCellar is a List<WineCellar>, WineCellar is not a string, so it does not make sense to perform an Except between these.
You could just as easily do,
// use HashSet for look up performance.
var exceptionWineSet = new HashSet<string>(exceptionWineList);
var result = orginalWineCellar.Where(w => !exceptionWineSet.Contains(w.Wine));
What I think you are alluding to in your question is something like
WineCellar : IEquatable<string>
{
...
public bool Equals(string other)
{
return other.Equals(this.wine, StringComparison.Ordinal);
}
}
which allows you to equate WineCellars to strings.
However, if I were to rework your model I'd come up with something like,
enum WineColour
{
Red,
White,
Rose
}
enum WineRegion
{
Bordeaux,
Rioja,
Alsace,
...
}
enum GrapeVariety
{
Cabernet Sauvignon,
Merlot,
Ugni Blanc,
Carmenere,
...
}
class Wine
{
public string Name { get; set; }
public string Vineyard { get; set; }
public WineColour Colour { get; set; }
public WineRegion Region { get; set; }
public GrapeVariety Variety { get; set; }
}
class WineBottle
{
public Wine Contents { get; set; }
public int Millilitres { get; set; }
public int? vintage { get; set; }
}
class Bin : WineBottle
{
int Number { get; set; }
int Quantity { get; set; }
}
class Cellar : ICollection<WineBottle>
{
...
}
Then, you can see that there are several ways to compare Wine and I may want to filter a Cellar on one or more of Wine's properties. Therefore I might be temtpted to give myself some flexibility,
class WineComparer : EqualityComparer<Wine>
{
[Flags]
public Enum WineComparison
{
Name = 1,
Vineyard= 2,
Colour = 4,
Region = 8,
Variety = 16,
All = 31
}
private readonly WineComparison comparison;
public WineComparer()
: this WineComparer(WineComparison.All)
{
}
public WineComparer(WineComparison comparison)
{
this.comparison = comparison;
}
public override bool Equals(Wine x, Wine y)
{
if ((this.comparison & WineComparison.Name) != 0
&& !x.Name.Equals(y.Name))
{
return false;
}
if ((this.comparison & WineComparison.Vineyard) != 0
&& !x.Vineyard.Equals(y.Vineyard))
{
return false;
}
if ((this.comparison & WineComparison.Region) != 0
&& !x.Region.Equals(y.Region))
{
return false;
}
if ((this.comparison & WineComparison.Colour) != 0
&& !x.Colour.Equals(y.Colour))
{
return false;
}
if ((this.comparison & WineComparison.Variety) != 0
&& !x.Variety.Equals(y.Variety))
{
return false;
}
return true;
}
public override bool GetHashCode(Wine obj)
{
var code = 0;
if ((this.comparison & WineComparison.Name) != 0)
{
code = obj.Name.GetHashCode();
}
if ((this.comparison & WineComparison.Vineyard) != 0)
{
code = (code * 17) + obj.Vineyard.GetHashCode();
}
if ((this.comparison & WineComparison.Region) != 0)
{
code = (code * 17) + obj.Region.GetHashCode();
}
if ((this.comparison & WineComparison.Colour) != 0)
{
code = (code * 17) + obj.Colour.GetHashCode();
}
if ((this.comparison & WineComparison.Variety) != 0)
{
code = (code * 17) + obj.Variety.GetHashCode();
}
return code;
}
}
this probably looks like a lot of effort but it has some use. Lets say we wanted all the wine except the Red Rioja in your cellar, you could do something like,
var comparison = new WineComparer(
WineComparison.Colour + WineComparison.Region);
var exception = new Wine { Colour = WineColour.Red, Region = WineRegion.Rioja };
var allButRedRioja = cellar.Where(c =>
!comparison.Equals(c.Wine, exception));
I had this exact same issue to. I tried the example from Darren but couldn't get that to work properly.
I therefore made a modification from DarrenĀ“s example as follows:
static class Helper
{
public static IEnumerable<Product> Except(this List<Product> x, List<Product> y)
{
foreach(var xi in x)
{
bool found = false;
foreach (var yi in y) { if(xi.Name == yi.Name) { found = true; } }
if(!found) { yield return xi; }
}
}
}
This works for me. You can possibly add several fields in the if clause if needed.
To directly use such extension methods with generic classes you should implement comparator. It consists of two methods: Equal and GetHashCode. You should implement them in your WineCellar class.
Note the second example.
Note that the hash-based methods are much faster than basic 'List.Contains...' implementations.

Variable initalisation in while loop

I have a function that reads a file in chunks.
public static DataObject ReadNextFile(){ ...}
And dataobject looks like this:
public DataObject
{
public string Category { get; set; }
// And other members ...
}
What I want to do is the following basically
List<DataObject> dataObjects = new List<DataObject>();
while(ReadNextFile().Category == "category")
{
dataObjects.Add(^^^^^ the thingy in the while);
}
I know it's probably not how it's done, because how do I access the object I've just read.
I think what you're looking for is:
List<DataObject> dataObjects = new List<DataObject>();
DataObject nextObject;
while((nextObject = ReadNextFile()).Category == "category")
{
dataObjects.Add(nextObject);
}
But I wouldn't do that. I'd write:
List<DataObject> dataObject = source.ReadItems()
.TakeWhile(x => x.Category == "Category")
.ToList();
where ReadItems() was a method returning an IEnumerable<DataObject>, reading and yielding one item at a time. You may well want to implement it with an iterator block (yield return etc).
This is assuming you really want to stop reading as soon as you find the first object which has a different category. If you actually want to include all the matching DataObjects,
change TakeWhile to Where in the above LINQ query.
(EDIT: Saeed has since deleted his objections to the answer, but I guess I might as well leave the example up...)
EDIT: Proof that this will work, as Saeed doesn't seem to believe me:
using System;
using System.Collections.Generic;
public class DataObject
{
public string Category { get; set; }
public int Id { get; set; }
}
class Test
{
static int count = 0;
static DataObject ReadNextFile()
{
count++;
return new DataObject
{
Category = count <= 5 ? "yes" : "no",
Id = count
};
}
static void Main()
{
List<DataObject> dataObjects = new List<DataObject>();
DataObject nextObject;
while((nextObject = ReadNextFile()).Category == "yes")
{
dataObjects.Add(nextObject);
}
foreach (DataObject x in dataObjects)
{
Console.WriteLine("{0}: {1}", x.Id, x.Category);
}
}
}
Output:
1: yes
2: yes
3: yes
4: yes
5: yes
In other words, the list has retained references to the 5 distinct objects which have been returned from ReadNextFile.
This is subjective, but I hate this pattern (and I fully recognize that I am in the very small minority here). Here is how I do it when I need something like this.
var dataObjects = new List<DataObject>();
while(true) {
DataObject obj = ReadNextFile();
if(obj.Category != "category") {
break;
}
dataObjects.Add(obj);
}
But these days, it is better to say
List<DataObject> dataObjects = GetItemsFromFile(path)
.TakeWhile(x => x.Category == "category")
.ToList();
Here, of course, GetItemsFromFile reads the items from the file pointed to by path and returns an IEnumerable<DataObject>.
List<DataObject> dataObjects = new List<DataObject>();
string category = "";
while((category=ReadNextFile().Category) == "category")
{
dataObjects.Add(new DataObject{Category = category});
}
And if you have more complicated object you can do this (like jon):
List<DataObject> dataObjects = new List<DataObject>();
var category = new DataObject();
while((category=ReadNextFile()).Category == "category")
{
dataObjects.Add(category);
}
You should look into implementing IEnumerator on the class container the call to ReadNextFile(). Then you would always have reference to the current object with IEnumerator.Current, and MoveNext() will return the bool you are looking for to check for advancement. Something like this:
public class ObjectReader : IEnumerator<DataObject>
{
public bool MoveNext()
{
// try to read next file, return false if you can't
// if you can, set the Current to the returned DataObject
}
public DataObject Current
{
get;
private set;
}
}

Sorting a List of objects in C#

public class CarSpecs
{
public String CarName { get; set; }
public String CarMaker { get; set; }
public DateTime CreationDate { get; set; }
}
This is a list and I am trying to figure out an efficient way to sort this list List CarList, containing 6(or any integer amount) Cars, by the Car Make Date. I was going to do Bubble sort, but will that work? Any Help?
Thanks
The List<T> class makes this trivial for you, since it contains a Sort method. (It uses the QuickSort algorithm, not Bubble Sort, which is typically better anyway.) Even better, it has an overload that takes a Comparison<T> argument, which means you can pass a lambda expression and make things very simple indeed.
Try this:
CarList.Sort((x, y) => DateTime.Compare(x.CreationDate, y.CreationDate));
You could use LINQ:
listOfCars.OrderBy(x => x.CreationDate);
EDIT: With this approach, its easy to add on more sort columns:
listOfCars.OrderBy(x => x.CreationDate).ThenBy(x => x.Make).ThenBy(x => x.Whatever);
The best approach is to implement either IComparable or IComparable<T>, and then call List<T>.Sort(). This will do all the hard work of sorting for you.
Another option would be to use a custom comparer:
using System;
using System.Collections.Generic;
using System.Text;
namespace Yournamespace
{
class CarNameComparer : IComparer<Car>
{
#region IComparer<Car> Members
public int Compare(Car car1, Car car2)
{
int returnValue = 1;
if (car1 != null && car2 == null)
{
returnValue = 0;
}
else if (car1 == null && car2 != null)
{
returnValue = 0;
}
else if (car1 != null && car2 != null)
{
if (car1.CreationDate.Equals(car2.CreationDate))
{
returnValue = car1.Name.CompareTo(car2.Name);
}
else
{
returnValue = car2.CreationDate.CompareTo(car1.CreationDate);
}
}
return returnValue;
}
#endregion
}
}
which you call like this:
yourCarlist.Sort(new CarNameComparer());
Note: I didn't compile this code so you might have to remove typo's
Edit: modified it so the comparer compares on creationdate as requested in question.
I would just use the build in List.Sort method. It uses the QuickSort algorithm which on average runs in O(n log n).
This code should work for you, I change your properties to auto-properties, and defined a static CompareCarSpecs method that just uses the already existing DateTime.CompareTo method.
class Program
{
static void Main(string[] args)
{
List<CarSpecs> cars = new List<CarSpecs>();
cars.Sort(CarSpecs.CompareCarSpecs);
}
}
public class CarSpecs
{
public string CarName { get; set; }
public string CarMaker { get; set; }
public DateTime CreationDate { get; set; }
public static int CompareCarSpecs(CarSpecs x, CarSpecs y)
{
return x.CreationDate.CompareTo(y.CreationDate);
}
}
Hope this helps.
Putting some of the pieces mentioned here together. This compiles and works in C# 4.x and VS2010. I tested with a WinForm. So add the method to the WinForm Main(). You will need the System.Linq and System.Generic.Collections assemblies at least.
private void SortCars()
{
List<CarSpecs> cars = new List<CarSpecs>();
List<CarSpecs> carsSorted = new List<CarSpecs>();
cars.Add(new CarSpecs
{
CarName = "Y50",
CarMaker = "Ford",
CreationDate = new DateTime(2011, 4, 1),
});
cars.Add(new CarSpecs
{
CarName = "X25",
CarMaker = "Volvo",
CreationDate = new DateTime(2012, 3, 1),
});
cars.Add(new CarSpecs
{
CarName = "Z75",
CarMaker = "Datsun",
CreationDate = new DateTime(2010, 5, 1),
});
//More Comprehensive if needed
//cars.OrderBy(x => x.CreationDate).ThenBy(x => x.CarMaker).ThenBy(x => x.CarName);
carsSorted.AddRange(cars.OrderBy(x => x.CreationDate));
foreach (CarSpecs caritm in carsSorted)
{
MessageBox.Show("Name: " +caritm.CarName
+ "\r\nMaker: " +caritm.CarMaker
+ "\r\nCreationDate: " +caritm.CreationDate);
}
}
}
public class CarSpecs
{
public string CarName { get; set; }
public string CarMaker { get; set; }
public DateTime CreationDate { get; set; }
}
If you're after an efficient way of sorting, I'd advise against using bubble sort and go for a quick sort instead. This page provides a rather good explanation of the algorithm:
http://www.devhood.com/Tutorials/tutorial_details.aspx?tutorial_id=574
Best of luck!
I would avoid writing my own sorting algorithm, but if you are going to anyway, have a look at http://www.sorting-algorithms.com/ for some comparrisons of different sorting algorithms...
If you are using 2.0, the following discussion may be useful: C# List<> Sort by x then y
If you use delegates (also known as anonymous methods), you won't have to implement any IComparer / IComparable interfaces.
public static void Main(string[] args)
{
List<CarSpecs> list = new List<CarSpecs>();
list.Add(new CarSpecs("Focus", "Ford", new DateTime(2010,1, 2));
list.Add(new CarSpecs("Prius", "Toyota", new DateTime(2012,3, 3));
list.Add(new CarSpecs("Ram", "Dodge", new DateTime(2013, 10, 6));
list.Sort(delegate (CarSpecs first, CarSpecs second)
{
int returnValue = 1;
if((first != null & second != null))
{
if (first.CarName.Equals(second.CarName))
{
if (first.CarMaker.Equals(second.CarMaker))
{
returnValue = first.CreationDate.CompareTo(second.CreationDate);
}
else
{
returnValue = first.CarMaker.CompareTo(second.CarMaker);
}
}
else
{
returnValue = first.CarName.CompareTo(second.CarName);
}
}
return returnValue;
});
}
To extend the answer of Noldorin, in order to sort a list with int datatype this can be used:
listName.Sort((x, y) => x.CompareTo(y));
Or if you have a complex object in the list:
inventoryList.Sort((x, y) => x.stockNumber.CompareTo(y.stockNumber));

Categories

Resources