Got Customer class which has Country property which has string property Name.
Also Customer implements IComparable<Country> like so:
public int CompareTo(Country other)
{
return string.Compare(this.Name, other.Name);
}
Now:
var custList = new List<Customer>{...};
custList.OrderBy(cust => cust.Country).ToList(); //Sorts as charm.
And if try sorting via reflection:
var itemProp = typeof(Customer).GetProperty("Country");
custList = c.Customers.ToList()
.OrderBy(cust => itemProp.GetValue(cust, null)).ToList(); // Fails
Throws exception 'At least one object must implement IComparable'
Please explain why does it fail and how correctly implement sorting of Customer by custom property via reflection. Thanks.
Since GetValue returns Object you need to implement the non generic version of IComparable.
void Main()
{
var custList = new List<Customer>()
{
new Customer(){ Country = new Country(){ Name = "Sweden" } },
new Customer(){ Country = new Country(){ Name = "Denmark" } },
};
var itemProp = typeof(Customer).GetProperty("Country");
custList = custList.OrderBy(cust => itemProp.GetValue(cust, null)).ToList();
custList.Dump();
}
public class Country : IComparable<Country>, IComparable
{
public string Name {get;set;}
public int CompareTo(Country other)
{
return string.Compare(this.Name, other.Name);
}
public int CompareTo(object other)
{
var o = other as Country;
if(o == null)
return 0; //Or how you want to handle it
return CompareTo(o);
}
}
public class Customer
{
public Country Country{get;set;}
}
Assuming that the underlying type is correct (i.e. Country), you should be able to do it as long as Country implements IComparable:
Here's a sample console app that works correctly (note that there is no error handling):
using System;
using System.Collections.Generic;
using System.Linq;
namespace Demo
{
class Number: IComparable<Number>, IComparable
{
public Number(int value)
{
Value = value;
}
public readonly int Value;
public int CompareTo(Number other)
{
return Value.CompareTo(other.Value);
}
public int CompareTo(object obj)
{
return CompareTo((Number) obj);
}
}
class Test
{
public Number Number;
public object Obj
{
get { return Number; }
}
public override string ToString()
{
return Number.Value.ToString();
}
}
internal static class Program
{
static void Main()
{
var itemProp = typeof(Test).GetProperty("Obj");
Console.WriteLine(string.Join("\n",
data().OrderBy(x => itemProp.GetValue(x, null))));
}
static IEnumerable<Test> data()
{
for (int i = 0; i < 10; ++i)
yield return new Test {Number = new Number(10-i)};
}
}
}
Related
I'm coding a simple console app as below and I'm getting the following error:
'SortedList<int, Employee>' does not contain a definition for 'CopyTo' and no accessible extension method 'CopyTo' accepting a first argument of type 'SortedList<int, Employee>'
This is in the getAllEmployeesistAll() method. AS part of the assignment, the method must return an Employee[] return type, hence the need to cast.
Appreciate any help!
Link to docs: https://learn.microsoft.com/en-us/dotnet/api/system.collections.sortedlist.copyto?view=net-6.0
When I put in a second argument (for the index start), I get an error saying no overload 2 arguments.
using System.Collections;
using System;
namespace Collections
{
public class Employee
{
private string employeeName;
private int employeeId;
private double salary;
public string EmployeeName
{
get { return employeeName; }
set { employeeName = value; }
}
public int EmployeeId
{
get { return employeeId; }
set { employeeId = value; }
}
public double Salary
{
get { return salary; }
set { salary = value; }
}
}
public class EmployeeDAL
{
SortedList<int, Employee> employees = new SortedList<int, Employee>();
public bool AddEmployee(Employee e)
{
try
{
employees.Add(e.EmployeeId, e);
return true;
}
catch
{
return false;
}
}
public bool DeleteEmployee(int id)
{
try
{
employees.RemoveAt(id - 1);
return true;
}
catch
{
return false;
}
}
public string SearchEmployee(int id)
{
var employee = employees[id - 1];
return $"You have searched for: Employee ID #{employee.EmployeeId} - Name: {employee.EmployeeName}\nSalary: S${employee.Salary}";
}
public Employee[] GetAllEmployeesistAll()
{
Employee[] employeeArr = new Employee[employees.Count];
employees.CopyTo(employeeArr);
foreach (Employee e in employeeArr)
Console.WriteLine($"Employee ID {e.EmployeeId}: {e.EmployeeName}, salary: S${e.Salary}");
return employeeArr;
}
}
public class Program
{
public static void Main(string[] args)
{
Employee ash = new Employee();
ash.EmployeeName = "Ash";
ash.EmployeeId = 1;
ash.Salary = 100000;
Employee lucy = new Employee();
lucy.EmployeeName = "Lucy";
lucy.EmployeeId = 2;
lucy.Salary = 200000;
EmployeeDAL employeeDAL = new EmployeeDAL();
employeeDAL.AddEmployee(ash);
employeeDAL.AddEmployee(lucy);
Console.WriteLine(employeeDAL.SearchEmployee(1));
//employeeDAL.DeleteEmployee(2);
employeeDAL.GetAllEmployeesistAll();
}
}
}
The link in the question go to the class ListedSorted, but the error is about the class ListedSorted<TKey, TValue>.
See this documentation, the class ListedSorted<TKey, TValue> don't have a method CopyTo.
To convert ListedSorted<TKey, TValue> in TValue[], you can use LINQ method ToArray :
var employees = new SortedList<int, Employee>();
var employeesArray = employees.Values.ToArray();
So that's my function:
public bool CheckUniqueName<T>(string newName, List<T> list)
{
for (int i = 0; i < list.Count(); i++)
{
if (list[i].name == newName)
{
return false;
}
}
return true;
}
I have this List of Planets: private List<Planet> planetsList = new List<Planet>();
BUT: I'm gonna use other Lists like public List<Colony> ColonyList = new List<Colony>(); That's why I need List<T>
And class Planet:
class Planet
{
...
public string name { get; }
...
}
And I try this: (some stuff) CheckUniqueName(name, planetsList) in other class
As I get it, List<T> doesn't know about the .name attribute.
I tried to create another List and do something like this:
public bool CheckUniqueName<T>(string newName, List<T> list)
{
if (list is List<Planet>)
{
var newList = planetsList;
}
for (int i = 0; i < list.Count(); i++)
{
if (list[i].name == newName)
{
return false;
}
}
return true;
}
It didn't work and same things with creating new List didn't work as well.
You can using generic constraints here:
public bool CheckUniqueName<T>(string newName, IEnumerable<T> items)
where T : INamed
=> !items.Any(i => (i.Name == newName));
public interface INamed
{
public Name { get; }
}
public class Planet : INamed
{
public Name { get; }
public Plant(string name)
{
Name = name;
}
}
public class Colony : INamed
{
public Name { get; }
public Colony(string name)
{
Name = name;
}
}
Another way to do this is to pass a delegate which knows how to fetch the name property off any type you pass in:
public bool CheckUniqueName<T>(IEnumerable<T> items, string newName, Func<T, string> nameSelector)
{
foreach (var item in items)
{
string name = nameSelector(item);
if (name == newName)
{
return false;
}
}
return true;
}
Call it like this:
CheckUniqueName(planetsList, "name", planet => planet.name);
Then your name property doesn't have to be called name -- it can be called whatever you want.
I wrote a long version of the CheckUniqueName method for clarity, but you could shorten it using linq:
public bool CheckUniqueName<T>(IEnumerable<T> items, string newName, Func<T, string> nameSelector)
{
return !items.Any(item => newName == nameSelector(item));
}
However once you go this far, you might as well discard the CheckUniqueName method entirely, and instead just write:
!plantsList.Any(x => x.name == "name");
I'm using NMock2 (2.1.3641.27570) in my unit tests.
IList<MyObj> values = _mock.NewMock<IList<MyObj>>();
That mock I return when my tested object calls the Values get property on my mocked object:
Expect.AtLeastOnce.On(_myKeepMock).GetProperty("Values").Will(Return.Value(values));
Then I expect value which is a MyObj to be added to my list values:
Expect.AtLeastOnce.On(values).Method("Add").With(value);
In order to avoid unexpected invocation of list'1.Add upon execution I understand I have to override the Equals method in the MyObj class:
public override bool Equals(object obj) { ...}
and compare by value instead of reference. But it doesn't even get called when executing the test (breakpoint not hit).
What do I need to do in order to pass the test when the item added to the list in the call is equal in value to the one added by the tested object?
I read about custom matchers but not sure if those apply here.
UPDATE
Full example:
using System.Collections.Generic;
using System.Linq;
using NMock2;
using NUnit.Framework;
public class Data
{
public int Val { get; set; }
public Data(int val) { Val = val; }
}
public class ModData
{
public int Val { get; set; }
protected bool Equals(ModData other)
{
return this.Val.Equals(other.Val);
}
public override int GetHashCode()
{
return this.Val.GetHashCode();
}
public override bool Equals(object obj)
{
ModData m = (ModData)obj;
return m != null && this.Val == m.Val;
}
}
public interface IAllData
{
IList<Data> Data { get; set; }
IList<ModData> ModData { get; set; }
}
public class AllData : IAllData
{
public IList<Data> Data { get; set; }
public IList<ModData> ModData { get; set; }
}
public class Calco
{
private IAllData _allData;
public Calco(IAllData allData)
{
_allData = allData;
}
public void Sum()
{
_allData.ModData.Add(new ModData { Val = _allData.Data.Sum(d => d.Val) });
}
}
public class CalcoTest
{
[Test]
public void Test()
{
Mockery mockery = new Mockery();
IList<Data> data = new List<Data>();
IList<ModData> modData = mockery.NewMock<IList<ModData>>();
IAllData allData = mockery.NewMock<IAllData>();
ModData modDatum = new ModData { Val = 4 };
data.Add(new Data(1));
data.Add(new Data(10));
Calco c = new Calco(allData);
Expect.AtLeastOnce.On(allData).GetProperty("Data").Will(Return.Value(data));
Expect.AtLeastOnce.On(allData).GetProperty("ModData").Will(Return.Value(modData));
Expect.AtLeastOnce.On(modData).Method("Add").With(modDatum);
c.Sum();
mockery.VerifyAllExpectationsHaveBeenMet();
}
}
Output:
NMock2.Internal.ExpectationException : unexpected invocation of list`1.Add(<WRM.Common.RiskCalculation.Tests.ModData>)
Expected:
at least 1 time: allData.Data, will return <System.Collections.Generic.List`1[WRM.Common.RiskCalculation.Tests.Data]> [called 1 time]
at least 1 time: allData.ModData, will return <list`1> [called 1 time]
at least 1 time: list`1.Add(equal to <WRM.Common.RiskCalculation.Tests.ModData>) [called 0 times]
Notice how it expects invocation of list'1.Add(<WRM.Common.RiskCalculation.Tests.ModData>)
and then shows it didn't call list'1.Add(<WRM.Common.RiskCalculation.Tests.ModData>)
Custom Matchers ARE the answer: http://nmock.sourceforge.net/advanced.html
public class IsMod
{
public static Matcher Equal(ModData otherMod)
{
return new ModMatcher(otherMod);
}
}
internal class ModMatcher : Matcher
{
private readonly ModData _mod;
public ModMatcher(ModData mod)
{
_mod = mod;
}
public override bool Matches(object o)
{
ModData m = (ModData)o;
return _mod.Val.Equals(m.Val);
}
public override void DescribeTo(TextWriter writer)
{
writer.Write("Value same ");
writer.Write(_mod.Val);
}
}
And then
Expect.AtLeastOnce.On(modData).Method("Add").With(IsMod.Equal(modDatum));
Done!
I have 2 lists that I need to check for common objects that are being passed to a generic wrapper.
The first list (selList) is a typed entity list. The ID field in this list is different, based on what the base type for the list being created.
The second list (masterList) is an anonymous IList that I know has 2 properties {ID, DESC} - ID (could be int or string), and description (string). I can get the value of the ID property in this list.
I would like to return an extension of the master list that has a boolean field indicating whether the item in the master list is contained in the selList.
I'm thinking that I'm somewhere along the lines of the Visitor pattern.
public class SelectionCriteria<T> : where T : class
{
public IList<T> EligibleList { get; private set; }
public IList LookupList { get; private set; }
}
LookupList = new List<object>
{
new { ID = "fid", DESC = "Record 1"},
new { ID = "Record2", DESC = "Record 2"},
new { ID = "Record3", DESC = "Record 3"},
new { ID = "Record4", DESC = "Record 4"},
};
EligibleList = new List<AssetClass>
{
new AssetClass { FEE_ID = "fid", ASSET_CLASS = "A" },
};
I should get the following results:
LookupList[0] == true
LookupList[1] == false
LookupList[2] == false
LookupList[3] == false
Is there a better way to solve this problem?
var results = LookupList.Select(l => EligibleList.Any(e => e.FEE_ID==l.ID))
.ToList();
Using this as a definition for SelectionCriteria<T>
public class SelectionCriteria<T>
where T : class
{
public IList<T> EligibleList { get; private set; }
public IList LookupList { get; private set; }
public SelectionCriteria(IList lookupList, IList<T> eligibleList)
{
LookupList = lookupList;
EligibleList = eligibleList;
}
public bool this[int index]
{
get
{
var element = LookupList[index];
foreach (var item in EligibleList)
{
if (item.Equals(element))
{
return true;
}
}
return false;
}
}
}
And this as a definition for AssetClass
public class AssetClass : IEquatable<AssetClass>
{
public string FEE_ID { get; set; }
public string ASSET_CLASS { get; set; }
public bool Equals(AssetClass other)
{
return !ReferenceEquals(other, null) && other.FEE_ID == FEE_ID && other.ASSET_CLASS == ASSET_CLASS;
}
//Check to see if obj is a value-equal instance of AssetClass, if it's not, proceed
// to doing some reflection checks to determine value-equality
public override bool Equals(object obj)
{
return Equals(obj as AssetClass) || PerformReflectionEqualityCheck(obj);
}
//Here's where we inspect whatever other thing we're comparing against
private bool PerformReflectionEqualityCheck(object o)
{
//If the other thing is null, there's nothing more to do, it's not equal
if (ReferenceEquals(o, null))
{
return false;
}
//Get the type of whatever we got passed
var oType = o.GetType();
//Find the ID property on it
var oID = oType.GetProperty("ID");
//Get the value of the property
var oIDValue = oID.GetValue(o, null);
//If the property type is string (so that it matches the type of FEE_ID on this class
// and the value of the strings are equal, then we're value-equal, otherwise, we're not
return oID.PropertyType == typeof (string) && FEE_ID == (string) oIDValue;
}
}
You can get elements that are found in the list of eligible items that exist in the list of lookup items like so:
for (var i = 0; i < assetClassSelectionCriteria.LookupList.Count; ++i)
{
Console.WriteLine("LookupList[{0}] == {1}", i, assetClassSelectionCriteria[i]);
}
You could also use the following for PerformReflectionEqualityCheck in AssetClass if you don't like seeing the reflection goodness
private bool PerformReflectionEqualityCheck(object o)
{
if (ReferenceEquals(o, null))
{
return false;
}
dynamic d = o;
try
{
return FEE_ID == (string) d.ID;
}
catch
{
return false;
}
}
If by "extension of the master list" you meant an extension method, then, instead of declaring an indexer on SelectionCriteria<T> to get the results, you could do something like this:
public static class SelectionCriteriaExtensions
{
public static bool IsLookupItemEligible<T>(this SelectionCriteria<T> set, int index)
where T : class
{
var element = set.LookupList[index];
foreach (var item in set.EligibleList)
{
if (item.Equals(element))
{
return true;
}
}
return false;
}
}
and call it like this:
assetClassSelectionCriteria.IsLookupItemEligible(0);
What I'm doing now:
void Main()
{
var command1 = new PersistenceCommand(new MyIntBO());
var command2 = new PersistenceCommand(new MyGuidBO());
var command3 = new PersistenceCommand(new PersistentBO());
Console.WriteLine(command1.ToString());
Console.WriteLine(command2.ToString());
Console.WriteLine(command3.ToString());
}
public class PersistenceCommand
{
public PersistenceCommand(PersistentBO businessObject)
{
_businessObject = businessObject;
}
public override string ToString()
{
string result = _businessObject.GetType().Name;
var keyed = _businessObject as IPrimaryKeyed<int>;
if (keyed != null)
{
result += " " + keyed.Id.ToString();
}
return result;
}
private readonly PersistentBO _businessObject;
}
public interface IPrimaryKeyed<out TKey>
{
TKey Id { get; }
}
public class PersistentBO {}
public class MyIntBO : PersistentBO, IPrimaryKeyed<int>
{
public int Id { get { return 1008; } }
}
public class MyGuidBO : PersistentBO, IPrimaryKeyed<Guid>
{
public Guid Id
{
get
{
return new Guid("6135d49b-81bb-43d4-9b74-dd84c2d3cc29");
}
}
}
This prints:
MyIntBO 1008
MyGuidBO
PersistentBO
I'd like it to print:
MyIntBO 1008
MyGuidBO 6135d49b-81bb-43d4-9b74-dd84c2d3cc29
PersistentBO
What's the most elegant way to do that?
I want to support all types of keys - int, long, Guid, etc. - so I'd rather not do multiple casts. Note that not every business object implements that interface (some do not have a single primary key).
I realize I could use reflection and try to access the Id property. I was wondering if there's a better solution.
Clarification: To address #Acaz Souza and #Petar Ivanov's answers, we have dozens of classes scattered over multiple assemblies that already implement IPrimaryKeyed<T>. I do not want to break all of them by extending the interface contract. If I were designing this from scratch, their solutions would work.
Just create a non-generic interface and replace the generic one with generic abstract class. Then check for the interface:
public interface IPrimaryKeyed
{
object ObjId { get; }
}
public abstract class PrimaryKeyed<TKey> : IPrimaryKeyed
{
public object ObjId { get { return Id; } }
public abstract TKey Id { get; }
}
---
public override string ToString()
{
string result = _businessObject.GetType().Name;
var keyed = _businessObject as IPrimaryKeyed;
if (keyed != null)
{
result += " " + keyed.ObjId.ToString();
}
return result;
}
Using reflection doesn't seem like a bad way to go here.
ToString method:
// for getting the Id prop
var identProp = _businessObject.GetType().GetProperty("Id");
string result = _businessObject.GetType().Name;
if (identProp != null)
{
result += " " + identProp.GetValue(_businessObject, null).ToString();
}
The problem is in that line:
var keyed = _businessObject as IPrimaryKeyed<int>;
Your other type is not IPrimaryKeyed<int> is IPrimaryKeyed<Guid>, then the if (keyed != null) is false.
You can try do this:
static void Main()
{
var command1 = new PersistenceCommand(new MyIntBO());
var command2 = new PersistenceCommand(new MyGuidBO());
var command3 = new PersistenceCommand(new PersistentBO());
Console.WriteLine(command1.ToString());
Console.WriteLine(command2.ToString());
Console.WriteLine(command3.ToString());
Console.ReadLine();
}
public class PersistenceCommand
{
public PersistenceCommand(PersistentBO businessObject)
{
_businessObject = businessObject;
}
public override string ToString()
{
string result = _businessObject.GetType().Name;
var keyed = _businessObject as IPrimaryKeyed;
if (keyed != null)
{
result += " " + keyed.Id.ToString();
}
return result;
}
private readonly PersistentBO _businessObject;
}
public interface IPrimaryKeyed
{
object Id { get; }
}
public class PersistentBO { }
public class MyIntBO : PersistentBO, IPrimaryKeyed
{
public object Id { get { return 1008; } }
}
public class MyGuidBO : PersistentBO, IPrimaryKeyed
{
public object Id { get { return new Guid("6135d49b-81bb-43d4-9b74-dd84c2d3cc29"); } }
}