I have the following:
List<Agenda> Timetable;
public class Agenda
{
public Object item; //item can be of any object type but has common properties
}
class MasterItem
{
public long ID;
}
class item1:MasterItem { //properties and methods here }
class item2:MasterItem { //properties and methods here }
At the start of the code, I have a list of item which I added using
item1 sItem = new item1() { //Initialize properties with values }
Timetable.Add(new Agenda {item = sItem );
Here I want to get Agenda with Item that has ID=12. I tried using
object x = Timetable.Find(delegate (Agenda a)
{
System.Reflection.PropertyInfo pinfo = a.item.GetType().GetProperties().Single(pi => pi.Name == "ID"); //returned Sequence contains no matching element
return ....
}
Why does it return the error message "Sequence contains no matching element"?
I also tried
a.item.GetType().GetProperty("ID")
but it returns "Object reference not set to an instance of an object". It cannot find the ID.
It's funny that don't get much from googling ...
You are looking for a property but what you have is a field. A property has get/get accessors than can contain custom code (but usually don't) whereas a field does not. You can change your class to:
public class Agenda
{
public Object item {get; set;} //item can be of any object type but has common properties
}
class MasterItem
{
public long ID {get; set;}
}
However, you state
item can be of any object type but has common properties
If that's the case, then you should define an interface that they all implement. That way, you don't need reflection:
public class Agenda
{
public ItemWithID item {get; set;}
}
Interface ItemWithID
{
long ID {get; set;}
}
class MasterItem : ItemWithID
{
public long ID {get; set;}
}
class item1:MasterItem { //properties and methods here }
class item2:MasterItem { //properties and methods here }
Your code assumes public properties. Is this the case? You have omitted the most important part of the sample code. Without it, we cannot reproduce your issue.
Regardless, reflection is the wrong approach here. You should use the following syntax:
Timetable.Find(delegate(ICommonPropeeties a) { return a.ID == 12; });
Where ICommonPropeties is an interface implemented by all items.
Related
I have several collections, each of specific class. I need that every instance (model) store its parent collection (to be able to reach other instances because of data validation). Trying to find some smart solution - starting to doubt if there is such.
So far I came up with following.
Generic base-class:
public class ModelBase<T> : ObservableObject, IDataErrorInfo
{
public CustomObservableCollection<T> ParentCollection;
public int Id { get; set; } // some generic prop
}
Individual classes (models):
public class Model1: ModelBase<Model1>
{
public string Name { get; set; } // Some specific prop
// Data Error validation here, e.g. check of uniqueness
if (ParentCollection?.Where(c => c.Id != Id).Any(c => c.Name == Name) == true)
{
}
}
public class Model2: ModelBase<Model2>
{}
Custom ObservableCollection:
public class CustomObservableCollection<T> : ObservableCollection<T> where T : ModelBase<T>
{
public CustomObservableCollection() : base() { }
public CustomObservableCollection(List<T> list)
: base(list)
{
foreach (T item in list)
{
item.ParentCollection = this;
}
}
public void AddEdit(T item)
{
item.ParentCollection = this;
base.Add(item);
}
protected override void InsertItem(int index, T item)
{
base.InsertItem(index, item);
item.ParentCollection = this;
// Initialize values
}
// Generic method - I would like to call this directly from command handler regardless of type T
public void MoveItemUp(object parameter)
{
}
}
Usage:
CustomObservableCollection<Model1> Model1Collection = new (listOfModel1instances);
I've been also trying to use Interfaces, but without success.
Thank you for useful reaction, IVSoftware.
I've tried to implement it according your example, but I ran into other issue I forget to mention in the beginning.
In my CustomObservableCollection class I would like to have some generic methods processing collection belonging to Item given by argument, e.g.
public int GetMaxId(t item)
{
return item.Source.Max(i => i.Id);
}
What I get:
Error CS1061 'ICollection' does not contain a definition for 'Max' ...
(Given example does not give much sense. But let's assume, that all items have Order property. In command handler I want to reorder items in collection, e.g. move selected item up/down. In command handler all I have is just selected item given by command parameter.
This would allow me to have just one Command for all types of items.)
Your question (with the additional information you provided) indicates that the items in your collection require access to the parent collection and must provide an Id property, for example so that the parent collection can use the linq Max method.
One way to solve both objectives is to define an interface like ISourceAware and constrain CustomObservableCollection<T> to accept only items that implement it by adding where T: ISourceAware to its declaration.
public interface ISourceAware
{
ICollection Source { get; set; }
string Id { get; }
}
public class CustomObservableCollection<T> : ObservableCollection<T> where T: ISourceAware
{
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
base.OnCollectionChanged(e);
if(e.Action.Equals(NotifyCollectionChangedAction.Add))
{
foreach (ISourceAware iitem in e.NewItems)
{
iitem.Source = this;
#if DEBUG
var item = (Item)iitem;
// Try a loopback test
Console.WriteLine(
$"Index of {item.Description} is {item.ParentCollection.IndexOf(item)}");
#endif
}
}
}
}
The collection handles its own CollectionChanged notification to set itself as the Source of any item added to it. This addresses the primary issue of storing their parent ObservableCollection - you can use it for validation however you see fit.
A minimal example of a class that implements ISourceAware:
public class Item : ISourceAware
{
public string Description { get; set; } = string.Empty;
public ICollection Source { get; set; }
public CustomObservableCollection<Item> ParentCollection =>
(CustomObservableCollection<Item>)Source;
public string Id { get; set; }
= Guid.NewGuid().ToString(); // Automatic unique initialization if you want it.
}
Test
Here's the quick console code I wrote to test this answer.
internal class Program
{
static void Main(string[] args)
{
Console.Title = "Test ISourceAware";
var collection = new CustomObservableCollection<Item>();
collection.Add(new Item { Description = "Item 1" });
collection.Add(new Item { Description = "Item 2" });
Console.ReadKey();
}
}
Given classes that are like these, is there a way to instantiate the Employee class and initialize the Generic List at runtime. Company rules preclude me from using dynamic, and using Reflection is frowned upon, but if there is no other way I can use it.
class Employee
{
public void SetList<T>(List<T> list) where T : IInputRow<T>
{
InputRows = list;
}
public List<T> InputRows;
public string EmployeeName {get; set;}
}
interface IInputRow<T>
{
T Parse(DataRow dr);
}
class JobRow : IInputRow<JobRow>
{
public int RowID {get; set;}
public string RowName {get; set;}
public JobRow Parse(DataRow dr)
{
//logic to convert datarow to entity
}
}
class VolunteerRow : IInputRow<VolunteerRow>
{
public int VolunteerRowID {get; set;}
public int VolunteerHours {get; set;}
public VolunteerRow Parse(DataRow dr)
{
//logic to convert datarow to entity
}
}
The list type has to be decided at run time.
I appreciate the comments and the answer, however, given that there are 46 different types of input rows I do not want to make the employee class generic as that would result in have to instantiate it for each input row that is needed for that round of processing. I might end up using reflection but I am somewhat hesitant about that given the sheer number of records that could conceivably be processed during a single run.
Try this Employee class:
public class Employee<T> where T : IInputRow<T>
{
public List<T> list;
public Employee()
{
list = new List<T>();
}
}
The <T> after class name is the magic. When you want a new Employee class with a List<JobRow>, you say Employee<JobRow> j = new Employee<JobRow>();.
Refer to Microsoft generic document for more info: https://learn.microsoft.com/en-us/dotnet/standard/generics/
Lets say I have a person class with name and id and a animal class with the same attributes, and i have a list with persons and animals. Now i want to make a method for returning the last id from that list and increment it. I want to make it universal so i can use it later.
public static int getNextId(List<Object>param)
{
int lastId = Int32.Parse(param[param.Count - 1].id);
if (lastId!=0)
{
return lastId++;
}
return 0;
}
but the 'id' is underlined because object does not have id.
Edit:
something like this in python
def someMethod(self, someList, attr):
objAttr = getattr(someObject, attr)
for(item in someList):
return item.objAttr
Your approach is not how you handle stuff like this in statically typed languages such as C#. You can only access properties / fields that are declared on the specific type. And object does not have a public field or property called id.
There are some ways around this: One would be having a base class that has an id property from which your other classes could inherit:
public class IdHolder
{
public int Id { get; set; }
}
public class Person : IdHolder
{
// Person inherits the 'Id' property from IdHolder
// other properties unique to person...
}
IdHolder could just as well be an interface or an abstract class - it depends on your specific use case (note that you would have to implement the Id property in each implementing class if you'd chose to make IdHolder an interface).
If you chose to have a base class (or interface, ...) you'd change your method to accept that as a parameter:
public static int getNextId(List<IdHolder>param)
{
int lastId = param[param.Count - 1].Id;
if (lastId!=0)
{
return lastId++;
}
return 0;
}
Another - slightly dirty - option is to use reflection. As I don't think that this is a sensible route for you to take, I won't go further into this here.
I'd advise you to have a look at an intro book into C# as there are some other aspects of your code that don't really follow C# guidelines (e. g. using camelCase instead of PascalCase).
Create a Person class with a Id Property like this:
public class Person
{
public int Id {get; set;}
}
So you can use:
public static int getNextId(List<Person>param)
{
int lastId = param.Last().Id;
if (lastId!=0)
{
return lastId++;
}
return 0;
}
Another aproach is using interfaces to make "universal" like you said:
public interface IId
{
int Id {get;set;}
}
public class Person : IId
{
public int Id {get; set;}
}
public static int getNextId(List<IId>param)
{
int lastId = param.Last().Id;
if (lastId!=0)
{
return lastId++;
}
return 0;
}
public static int GetNextId(List<object> param)
{
int id = param.OfType<Person>().Last().Id;
return ++id;
}
OfType method filters the elements based on a specified type. Only people will be filtered from the list of people and animals.
Last method returns a last element of a sequence.
I am making a save/load system for a big game project in C#.
Each class that has to be saved implements a method DoSnapshot().
Inside the method, the programmer must make a call to a function for every field in the class - either DoSnapshot(foo) if foo should be saved, or Ignore(foo) if it should not.
I have a DoSnapshot method for many primitive types like DoFloat, DoString as well as versions for complex types.
I have 100s of classes and the project is still being developed.
Is it possible to add some kind of verification that all of the fields in each class are either used in a Snapshot() or an Ignore() call? Omitting fields would cause bugs.
The verification could either be runtime, or compile-time. I only want to use it during development, it will not be released to users.
You could add an attribute to the fields that need to be saved, and then loop over every property in your class in the DoSnapshot method. When the property has the attribute you're looking for, you call Snapshot, otherwise you call Ignore.
public class SomeClass : SomeBaseClass
{
[Required]
public string Foo { get; set; }
public string Bar { get; set; }
public override void DoSnapshot()
{
var properties = this.GetType().GetProperties();
foreach (var property in properties)
{
var isRequired = property.GetCustomAttributes(typeof (RequiredAttribute), false).Length > 0;
if (isRequired)
{
// Something
}
else
{
// SomethingElse
}
}
}
}
What i would do is create an attribute and "tag" each field if it should be saved or not. Then, at runtime, i would query the class using reflection to get all fields which should be serialized:
public class RandomClass
{
public string Foo { get; set; }
[Ignore]
public int Bar { get; set; }
}
public class IgnoreAttribute : Attribute
{
}
class Program
{
static void Main(string[] args)
{
var properties = typeof(RandomClass).GetProperties()
.Where(prop => !prop.IsDefined(typeof(IgnoreAttribute), false));
// Serialize all values
}
}
In my model I have these entities:
public interface IOrder
{
string Name {get;set;}
List<IProduct> OrderedProducts {get;set;}
}
public interface IProduct {}
In partial class generated by linq-to-sql I map these properties on my entity properties:
public partial class Order : IOrder
{
List<IProduct> OrderedProducts
{
get { return this.L2SQLProducts.Cast<IProduct>.ToList(); }
set { this.L2SQLProducts = ??? }
}
}
How should setter look like?
EDIT: Purpose of this is to have interface that I could MOCK for unit testing or change linq-to-sql to another DAL.
汗了。。。
这样不怕性能问题吗?
如果我这样编码呢?
var a = xx.OrderedProducts[0];
var b = xx.OrderedProducts[1];
这样不是要该问了两次数据库吗?
translate to english:
what?
this can be slowly when run.
if I code like this:
var a = xx.OrderedProducts[0];
var b = xx.OrderedProducts[1];
Is that you will access to the database twice?
An EntitySet implements IList so rather than dealing with a derived List instance, why not use the interface IList and simply assign it directly, as in:
public interface IOrder
{
string Name {get;set;}
IList<IProduct> OrderedProducts {get;set;}
}
Alternatively, you could instantiate an entity set, and copy over the objects in the list to that entity set. There may be alternative consequences to this.
List<IProduct> OrderedProducts
{
get { return this.L2SQLProducts.Cast<IProduct>.ToList(); }
set {
this.L2SQLProducts = new EntitySet<IProduct>();
foreach (var entry in value)
this.L2SQLProducts.Add(entry);
}
}
HTH.