I have such a funny question.
I have the following architecture:
For example, Manager class is implemented like this:
public sealed class Manager : Interface.Abstract.Employee
{
private Interface.IEmployee chief = null;
private readonly Decimal bonuslimit = Convert.ToDecimal(0.4F * Convert.ToSingle(BaseSalary));
public Manager(Person person, DateTime hiredate)
: base(person, hiredate)
{
}
public override List<Interface.IEmployee> Subordinates
{
get;
set;
}
public override Interface.IEmployee Chief
{
get
{
return this.chief;
}
set
{
//if(value is Associate)
//{
// throw new SystemException("Associate can't be a chief");
//}
this.chief = value;
}
}
public override Decimal Salary
{
get
{
var actualbonus = Convert.ToDecimal(0.01F * Convert.ToSingle(this.YearsSinceHired * BaseSalary));
var bonus = (actualbonus > bonuslimit) ? bonuslimit : actualbonus;
var additional = 0M;
if(this.HasSubordinates)
{
foreach(Interface.Abstract.Employee employee in this.Subordinates)
{
if(employee is Sales)
{
additional += employee.Salary;
}
}
}
return Convert.ToDecimal(Convert.ToSingle(additional) * 0.005F) + BaseSalary + bonus;
}
}
}
And 'factory client' that looks like this:
public class EmployeeFactoryClient
{
private IDictionary<String, IEmployee> employees = new Dictionary<String, IEmployee>();
public EmployeeFactoryClient()
{
this.Factory = new EmployeeFactory();
}
public EmployeeFactoryClient(IEmployeeFactory factory)
{
this.Factory = factory;
}
public IEmployeeFactory Factory { get; set; }
public void HireEmployee(Person person, String type, String code)
{
this.employees.Add(
new KeyValuePair<String, IEmployee>(
code,
this.Factory.Create(person, type, DateTime.Now)
)
);
}
public void DismissEmployee(String code)
{
this.employees.Remove(code);
}
public IEmployee GetEmployee(String code)
{
return this.employees[code];
}
public IEmployee this[String index]
{
get { return this.employees[index]; }
private set { this.employees[index] = value; }
}
public Decimal TotalSalary
{
get
{
var result = 0M;
foreach(var item in this.employees)
{
result += item.Value.Salary;
}
return result;
}
}
}
And finally I have some test code:
public void SalaryTest()
{
#region [Persons]
var SalesPerson01 = new Person
{
Birthday = new DateTime(1980, 11, 03),
Forename = "Corey",
Surname = "Black",
Gender = SexType.Female
};
var SalesPerson02 = new Person
{
Birthday = new DateTime(1980, 11, 03),
Forename = "John",
Surname = "Travis",
Gender = SexType.Male
};
#endregion
this.company.HireEmployee(SalesPerson01, "Sales", SalesPerson01.GetHashCode().ToString());
((Employee)this.company[SalesPerson01.GetHashCode().ToString()]).YearsSinceHired = 10;
this.company.HireEmployee(SalesPerson02, "Sales", SalesPerson02.GetHashCode().ToString());
((Employee)this.company[SalesPerson02.GetHashCode().ToString()]).YearsSinceHired = 3;
///////////////////////////////////////////////////////////////////
((Employee)this.company[SalesPerson01.GetHashCode().ToString()]).Subordinates.Add(
this.company[SalesPerson02.GetHashCode().ToString()]
);
Assert.AreEqual(1405M, this.company.TotalSalary);
}
Line
((Employee)this.company[SalesPerson01.GetHashCode().ToString()]).Subordinates.Add(this.company[SalesPerson02.GetHashCode().ToString()]); throws NullReferenceExeption. In the this.company[SalesPerson02.GetHashCode().ToString()] indexer returns IEmployee interface but not a class instance. Am I right? And if it is so how do I fix that?
I don't see anywhere that you are initializing the Subordinates member, so I suspect that it still has the default value which is null (not the empty list). The fix is to initialize it to an empty list in the constructor:
public Manager(Person person, DateTime hiredate) : base(person, hiredate)
{
Subordinates = new List<Interface.IEmployee>();
}
it seems you're putting in Person but then casting to Employee which are unrelated classes
indexer returns IEmployee interface but not a class instance. Am I right?
You don't right indexer must return instance, create instance from interface impossible.
I think this.company[SalesPerson02.GetHashCode().ToString()] returns null cause you don't add SalesPerson02 instance to your company object.
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();
I'm using MVVM Light in Visual Studio 2015 to build an WPF app. There's a method in the code that's repeated with slight variation 4 times in the code; the only difference is the type of the ObservableCollection being modified and the method called on the data service layer.
Here's the method, which returns an ObservableCollection of StatusViewModel objects, which are used to populate a ComboBox; the StatusVm is used for binding to the SelectedItem of the ComboBox, is set as the first item in the collection and is "blank":
private async Task<ObservableCollection<StatusViewModel>> GetStatuses()
{
var result = new ObservableCollection<StatusViewModel>();
var blank = new StatusViewModel
{
StatusId = -1,
Status = null,
Description = null,
IsActive = false,
CreatedDate = DateTime.Now
};
result.Add(blank);
var dataService = new MyDataService();
foreach (var c in await dataService.GetStatuses())
result.Add(c);
StatusVm =
result.SingleOrDefault(c => c.StatusId.Equals(-1));
return result;
}
Here's the private field and public property for StatusVm:
private StatusViewModel _statusVm;
public StatusViewModel StatusVm
{
get { return _statusVm; }
set
{
if (Equals(value, _statusVm)) return;
_statusVm = value;
RaisePropertyChanged();
}
}
Now imagine the above being repeated 3 more times, with 3 more VM types! How do I make GetStatuses() into a method that can take different view model types and call the appropriate method on the data service? Thank you.
Update: Here are the property and method for another of the types:
private MroViewModel_mroVm;
public MroViewModel MroVm
{
get { return _mroVm; }
set
{
if (Equals(value, _mroVm)) return;
_mroVm = value;
RaisePropertyChanged();
}
}
private async Task<ObservableCollection<MroViewModel>> GetMro()
{
var result = new ObservableCollection<MroViewModel>();
var blank = new MroViewModel
{
StatusId = -1,
Status = null,
Description = null,
IsActive = false,
CreatedDate = DateTime.Now
};
result.Add(blank);
var dataService = new MyDataService();
foreach (var c in await dataService.GetMro())
result.Add(c);
MroVm =
result.SingleOrDefault(c => c.StatusId.Equals(-1));
return result;
}
You would create interface for common properties.
internal interface IStatusViewModel {
int StatusId { get; set; }
string Status { get; set; }
string Description { get; set; }
bool IsActive { get; set; }
DateTime CreatedDate { get; set; }
}
Implement the interface in classes that you need to retrieve status for
internal class MroViewModel : IStatusViewModel {
public int StatusId { get; set; }
public string Status { get; set; }
public string Description { get; set; }
public bool IsActive { get; set; }
public DateTime CreatedDate { get; set; }
}
Make method static and pass the service function which will call appropriate method to retrieve old statuses.
public static async Task<ObservableCollection<T>> GetStatuses<T>(
Func<MyDataService, Task<IEnumerable<T>>> retrieveStatusesAction)
where T : IStatusViewModel, new()
{
var result = new ObservableCollection<T>();
var blank = new T
{
StatusId = -1,
Status = null,
Description = null,
IsActive = false,
CreatedDate = DateTime.Now
};
result.Add(blank);
var dataService = new MyDataService();
foreach (var c in await retrieveStatusesAction(dataService))
result.Add(c);
// TODO Implement Expression<Func<TSource, TResult>> projection for assigning to VM
StatusVm = result.SingleOrDefault(c => c.StatusId.Equals(-1));
return result;
}
You would then call this method like so:
GetStatuses((service) => service.GetMro());
I didn't test this and the StatusVm needs to be assigned using expression compilation. I will take a look at how to do that now, but the idea is there.
For the Expression and property assigning:
Property selector Expression<Func<T>>. How to get/set value to selected property
-- EDIT --
Something like this for VM assignment:
public static async Task<ObservableCollection<T>> GetStatuses<T, TContainer>(
TContainer instance,
Expression<Func<TContainer, T>> viewModelProjection,
Func<MyDataService, Task<IEnumerable<T>>> retrieveStatusesAction)
where T : IStatusViewModel, new()
{
var result = new ObservableCollection<T>();
var blank = new T
{
StatusId = -1,
Status = null,
Description = null,
IsActive = false,
CreatedDate = DateTime.Now
};
result.Add(blank);
var dataService = new MyDataService();
foreach (var c in await retrieveStatusesAction(dataService))
result.Add(c);
var vmStatus = result.SingleOrDefault(c => c.StatusId.Equals(-1));
// Warning: Check casted values, this is unsafe
var vm = (PropertyInfo)((MemberExpression)viewModelProjection.Body).Member;
vm.SetValue(instance, vmStatus, null);
return result;
}
You would then call the method like this:
await GetStatuses(this, inst => inst.MroVm, (service) => service.GetMro());
If you are not familiar with expressions, I will explain.
First argument is object in which view model instance is located. Second argument selects the property from that object that corresponds to the view model that needs to be changed. Last argument is function that takes the service and returns appropriate method that retrieves the statuses - this is like pointer to the function in C++.
This will compile, but not sure if it will behave as expected. It should. If you have any problems, please write them down in comments.
You could define interfaces and try a combo of Strategy and Factory, like the following (I skipped async/await for simplicity):
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Starting");
var mainVm = new MainViewModel();
mainVm.GetStatuses();
mainVm.GetMro();
Console.WriteLine("Status: {0} {1}", mainVm.StatusVm.Name, mainVm.StatusVm.CreateDate);
Console.WriteLine("MroVm: {0} {1}", mainVm.MroVm.Name, mainVm.MroVm.CreateDate);
}
}
public class MainViewModel
{
public StatusViewModel StatusVm { get; set; }
public MroViewModel MroVm { get; set; }
public void GetStatuses()
{
var result = Get(VmKind.Status);
StatusVm = result.SingleOrDefault(c => c.StatusId.Equals(-1)) as StatusViewModel;
}
public void GetMro()
{
var result = Get(VmKind.Mro);
MroVm = result.SingleOrDefault(c => c.StatusId.Equals(-1)) as MroViewModel;
}
public IEnumerable<IVm> Get(VmKind vmKind)
{
var dataService = new MyDataService();
return dataService.Get(vmKind);
}
}
public interface IVm
{
int StatusId { get; set; }
DateTime CreateDate { get; set; }
string Name { get; }
}
public class StatusViewModel : IVm
{
public DateTime CreateDate { get; set; }
public int StatusId { get; set; }
public string Name { get { return "StatusViewModel"; } }
}
public class MroViewModel : IVm
{
public DateTime CreateDate { get; set; }
public int StatusId { get; set; }
public string Name { get { return "MroViewModel"; } }
}
public enum VmKind {Status, Mro }
#region Strategy
public interface IDataGetter
{
IEnumerable<IVm> Get(VmKind vmKind);
}
public class MyDataService : IDataGetter {
public IEnumerable<IVm> Get(VmKind vmKind)
{
switch (vmKind)
{
case VmKind.Status:
return GetStatuses();
//break;
case VmKind.Mro:
return GetMro();
//break;
default:
throw new ArgumentException("Unknown VM type");
}
}
private IEnumerable<IVm> GetMro()
{
return new List<MroViewModel> {
new MroViewModel { StatusId = -1, CreateDate = DateTime.Now },
new MroViewModel { StatusId = 2, CreateDate = DateTime.Now }
};
}
private IEnumerable<StatusViewModel> GetStatuses()
{
return new List<StatusViewModel> {
new StatusViewModel { StatusId = -1, CreateDate = DateTime.Now },
new StatusViewModel { StatusId = 2, CreateDate = DateTime.Now }
};
}
}
#endregion
#region Factory
public class VmFactory {
static IVm Create(VmKind vmKind)
{
IVm result = null;
switch (vmKind)
{
case VmKind.Status:
result = new StatusViewModel { StatusId = -1, CreateDate = DateTime.Now };
break;
case VmKind.Mro:
result = new MroViewModel { StatusId = -1, CreateDate = DateTime.Now };
break;
default:
throw new ArgumentException("Unknown VM type");
//break;
}
return result;
}
}
#endregion
I didn't actually use the Factory here, but you could do it for easy VM creation.
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)};
}
}
}
I have a collection of objects that I want to sort alphabetically by their name property.
I have tried the following:
List<Item> itemsToSort = dataProvider.Items.ToList();
List<Item> sortedItems = itemsToSort.OrderBy(x=>x.Name).ToList();
Which doesnt work. The items are still listed in the same way as before and not alphabetically.
EDIT
Here is a more complete sample:
Class:
public class MeasureStation
{
#region Properties
public int ID
{
get { return _measureStation.ID; }
set { _measureStation.ID = value; }
}
[Required(ErrorMessage = "Navn skal udfyldes")]
public String Name
{
get { return _measureStation.Name; }
set { _measureStation.Name = value; }
}
public DateTime? DateEstablished
{
get { return _measureStation.DateEstablished; }
set { _measureStation.DateEstablished = value; }
}
public DateTime? OperationPeriodStart
{
get { return _measureStation.OperationPeriodStart; }
set { _measureStation.OperationPeriodStart = value; }
}
.
.
and so on...
}
The query is:
measureStations = dataProvider.MeasureStations.ToList();
var orderedMeasureStations = measureStations.OrderBy(x => x.Name);
When taking a look at the orderedMeasureStations query result, it looks like the following:
entry starting with:
F...
S...
a...
L...
So it is obviously not sorting by name.
I do not see how your class MeasureStation will ever work
What is _measureStation, is it this?
If it is, Then it should look like, note that the property cannot have the same name as its respective private member
public class MeasureStation
{
private int id;//private is optional as it is default
public int ID
{
get { return this.id; }
set { this.id = value; }
}
private String name;//private is optional as it is default
public String Name
{
get { return this.name; }
set { this.name = value; }
}
}
Though, it is equivalent to the class with auto-properties:
public class MeasureStation
{
public int ID {get;set;}
public String Name {get;set;}
}
So, I run against both of them
private static void Main(string[] args)
{
List<MeasureStation> itemsToSort
= new List<MeasureStation>()
{
new MeasureStation() {ID = 01, Name = "Bbbb"},
new MeasureStation() {ID = 01, Name = "Aaaa"},
new MeasureStation() {ID = 01, Name = "Cccc"}
};
List<MeasureStation> sortedItems = itemsToSort.OrderBy(x => x.Name).ToList();
Console.WriteLine("******itemsToSort*****");
foreach (var item in itemsToSort)
Console.WriteLine(item.Name.ToString());
Console.WriteLine("******sortedItems*****");
foreach (var item in sortedItems)
Console.WriteLine(item.Name.ToString());
Console.ReadLine();
}
having gotten ordered output:
******itemsToSort*****
Bbbb
Aaaa
Cccc
******sortedItems*****
Aaaa
Bbbb
Cccc
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"); } }
}