I need to compare two instance, which are in same type.
public class Person
{
public int Age {get;set;}
public string Name { get;set;}
public static bool operator < (Person p1, Person p2)
{
return p1.Age < p2.Age;
}
public static bool operator > (Person p1, Person p2)
{
return p1.Age > p2.Age;
}
}
Now in Main i have the code:
Person o = new Person()
{
Age = 10,
Name = "Matin"
};
Func<Person, bool> test = person => person < o;
Person o2 = new Person()
{
Age = 9,
Name = "hehe"
};
Console.WriteLine(test(o2));
output: true;
and in my opinion, the 1st person is greater than the second. Could you explain me this situation?
Your code:
Func<Person, bool> test = person => person < o;
So you are passing the person and comparing it with the first one (o).
test(o2)
transforms to the
evaluate `o2 < o`
which is true, as o2.Age < o.Age (9 < 10).
You should implement IComparable like this:
public class Person : IComparable<Person>
{
public int Age { get; set; }
public string Name { get; set; }
public int CompareTo(Person other)
{
return this.Age.CompareTo(other.Age);
}
}
And use it like this:
Person o = new Person()
{
Age = 10,
Name = "Matin"
};
Person o2 = new Person()
{
Age = 9,
Name = "hehe"
}
Console.WriteLine(o.CompareTo(o2));
Your test is:
The person passed to the function must be less than o
In this case, "less than" means "has a lower age than".
Then you pass in o2, so let's see:
o2, age 9
o, age 10
So yes, o2 is less than o.
Output is correct.
In the call to test(o2), the object o2 is passed to test as parameter person.
In this way, the expression o2 < o is evaluated, which yields true, since the Age of o2 is 9, and the Age of o is 10.
The output is true because is exactly that your code says.
to do a good comparation you need two variables (at the moment, you are using all the time "o" instance to compare)
Do Func<Person, Person, bool> test = (person1, person2) => person1 < person2;
and then Console.WriteLine(test(o, o2));
Related
I am sorry I don't know if C# has this syntax or not and I don't know the syntax name. My code below, I want to add 2 people has the same name but not age. So I am wondering if C# has a brief syntax that I can change the Age property value when calling AddPerson method. Please tell me if I can do that? If can, how can I do? Thank you.
class Program
{
static void Main(string[] args)
{
//I want to do this
Person p = new Person
{
Name = "Name1",
//And other propeties
};
AddPerson(p{ Age = 20});
AddPerson(p{ Age = 25}); //Just change Age; same Name and others properties
//Not like this(I am doing)
p.Age = 20;
AddPerson(p);
p.Age = 25;
AddPerson(p);
//Or not like this
AddPerson(new Person() { Name = "Name1", Age = 20 });
AddPerson(new Person() { Name = "Name1", Age = 25 });
}
static void AddPerson(Person p)
{
//Add person
}
}
class Person
{
public string Name { get; set; }
public int Age { get; set; }
//And more
}
If Person is a Record-Type, then yes: by using C# 9.0's new with operator. Note that this requires C# 9.0, also note that Record-Types are immutable.
public record Person( String Name, Int32 Age );
public static void Main()
{
Person p = new Person( "Name1", 20 );
List<Person> people = new List<Person>();
people.Add( p with { Age = 25 } );
people.Add( p with { Age = 30 } );
people.Add( p with { Name = "Name2" } );
}
This is what I see in LinqPad when I run it:
I have a list of object like below
public class Person
{
public string Name {get; set;}
public int Age {get; set;}
}
public class SomeClass
{
public int DoSomething ()
{
int result;
List<Person> personList = new List<Person>();
personList.Add(new Person { //with 1 object, just to keep simple
Name = "Someone",
Age = 18});
Person eighteenYearsOld = _checkAge.FindEighteenYearsOld (personList);
int index = personList.IndexOf (eighteenYearsOld);
//do something
return result;
}
}
[TestMethod]
public void DoSomething_Test()
{
//Given:
//When: I call SomeClass object
Person eightnneYears = new Person {
Name = "Someone",
Age = 18};
_mockCheckAge.Setup (x => x.FindEighteenYearsOld(It.IsAny<List<Person>>())).Returns(eightnneYears);
_someClass = new SomeClass (_mockCheckAge.Object);
int result = _someClass.DoSomething();
//Then:
}
As I have mocked the FindEighteenYearsOld method it returns a Person object with same states which is presents in the personList. But when personList.IndexOf() executes it returns index -1, which is suppose to be 0. What should I do.
List.IndexOf uses Equals to find equal objects. If your class doesn't override it only references are compared. Since your two instances are not the same List.IndexOf returns -1.
So override Equals (and also always GetHashCode then):
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public override bool Equals (Object obj)
{
Person otherPerson = obj as Person;
return otherPerson != null
&& otherPerson.Name == Name
&& otherPerson.Age == Age;
}
// http://stackoverflow.com/a/263416/284240
public override int GetHashCode()
{
unchecked // Overflow is fine, just wrap
{
int hash = 17;
hash = hash * 23 + (Name?.GetHashCode() ?? 0); // or: hash = hash * 23 + (Name == null ? 0 : Name.GetHashCode());
hash = hash * 23 + Age; ;
return hash;
}
}
}
I have been digging this quite a while.
public class Person
{
public string Name { get; set; }
public string Age { get; set; }
public List<Person> Children { get; set; }
}
I want a single LINQ query to find out "All the persons whose Age > 4 in this collection".
Note: You have to traverse Collection of Person + Collection of Children, so each children object will have a collection of Person till Children becomes null.
First i can't understand why all your properties private and Age is not int type. So my class looks like this:
public partial class Person
{
public string Name { get; set; }
public int Age { get; set; }
public List<Person> Childrens { get; set; }
}
Note partial word. This word will allow you to place your class logic in separate files and this could be usefull when you creating some custom logic in class.
Then I simply create this method in different file:
public partial class Person
{
public Person GetPersonWithChindren(int maxAge)
{
return new Person
{
Age = this.Age,
Name = this.Name,
Childrens = this.Childrens != null
? this.Childrens
.Where(x => x.Age < maxAge)
.Select(x => x.GetPersonWithChindren(maxAge)) //this line do recursive magic
.ToList()
: null
};
}
}
As you can see this method checking Age of each child and if Age is ok then it checks next level of hierarchy untill Childrens is null.
So you can use it like this:
var person = new Person()
{
//initialisation of your collection here
}
//result will contains only nodes where Person have age < 4 and Childs that have age < 4
var result = person.GetPersonWithChindren(4);
Note that this solution will work normal with linqToEntities. But if you using LinqToSQL this expression produces query to DB on each Person entity. So if you have many Persons and deep hierarhy it will costs you a lot of machine time. In that case you should to write stored procedure with CTE instead of LinQ query.
UPDATE:
You even can write more general solution with a help of Func<T> class like this:
public partial class Person
{
public Person GetPersonWithChindren(Func<Person, bool> func)
{
return new Person
{
Age = this.Age,
Name = this.Name,
Childrens = this.Childrens != null
? this.Childrens
.Where(x => func(x))
.Select(x => x.GetPersonWithChindren(func))
.ToList()
: null
};
}
}
And then you can use it like this:
var result = person.GetPersonWithChindren(x => x.Age < 4);
You always can change your criteria now where you want to use your function.
Create a visitor. In this example by implementing a helper class:
public static class Helpers
public static IEnumerable<Person> GetDescendants(this Person person)
{
foreach (var child in person.Children)
{
yield return child;
foreach (var descendant in child.GetDescendants())
{
yield return descendant;
}
}
}
this is one of the times where the "yield return many" would be useful.
If you're ensuring that .Children is automatically created, then this works:
Func<Person, Func<Person, bool>, Person> clone = null;
clone = (p, f) => f(p) ? new Person()
{
Name = p.Name,
Age = p.Age,
Children = p.Children.Select(c => clone(c, f)).Where(x => x != null).ToList(),
} : null;
var olderThan4 = clone(person, p => p.Age > 4);
Yes, that's it. Effectively three lines.
If you start with this data:
var person = new Person()
{
Name = "Fred", Age = 30,
Children = new List<Person>()
{
new Person() { Name = "Bob", Age = 7, },
new Person() { Name = "Sally", Age = 3, }
},
};
...then you get this result:
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
private List<Person> _children = null;
public List<Person> Children
{
get
{
if (_children == null)
{
_children = new List<Person>();
}
return _children;
}
set
{
_children = value;
}
}
}
When i initialize an object using the new object initializers in C# I cannot use one of the properties within the class to perform a further action and I do not know why.
My example code:
Person person = new Person { Name = "David", Age = "29" };
Within the Person Class, x will equal 0 (default):
public Person()
{
int x = Age; // x remains 0 - edit age should be Age. This was a typo
}
However person.Age does equal 29. I am sure this is normal, but I would like to understand why.
The properties get set for Name and Age after the constructor 'public Person()' has finished running.
Person person = new Person { Name = "David", Age = "29" };
is equivalent to
Person tempPerson = new Person()
tempPerson.Name = "David";
tempPerson.Age = "29";
Person person = tempPerson;
So, in the constructor Age won't have become 29 yet.
(tempPerson is a unique variable name you don't see in your code that won't clash with other Person instances constructed in this way. tempPerson is necessary to avoid multi-threading issues; its use ensures that the new object doesn't become available to any other thread until after the constructor has been executed and after all of the properties have been initialized.)
If you want to be able to manipulate the Age property in the constructor, then I suggest you create a constructor that takes the age as an argument:
public Person(string name, int age)
{
Name = name;
Age = age;
// Now do something with Age
int x = Age;
// ...
}
Note, as an important technical detail, that:
Person person = new Person { Name = "David", Age = "29" };
is equivalent to:
Person <>0 = new Person(); // a local variable which is not visible within C#
<>0.Name = "David";
<>0.Age = "29";
Person person = <>0;
but is not equivalent to:
Person person = new Person();
person.Name = "David";
person.Age = "29";
Your line of code is identical to:
Person person = new Person() { Name = "David", Age = "29" };
which is identical to:
Person person = new Person();
person.Name = "David";
person.Age = "29";
As you can see; when the constructor executes, Age is not yet set.
Technically, this code:
Person person = new Person { Name = "David", Age = 29 };
is identical to this code:
Person tmpPerson = new Person();
tmpPerson.Name = "David";
tmpPerson.Age = 29;
Person person = tmpPerson;
which is slightly different than what others have posted:
Person person = new Person();
person.Name = "David";
person.Age = 29;
This difference is crucial if your application is using multi-threading.
It looks like you're trying to access Age in the object's constructor. The object initializer values won't be set until after the constructor has executed.
Try this:
Person person = new Person { Name = "David", Age = 29 };
int x = person.Age;
EDIT in response to comment
If you need access to Age in the constructor itself then you'll need to create an explicit constructor with the required parameters, and use that instead of the object initializer syntax. For example:
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public Person(string name, int age)
{
Name = name;
Age = age;
int x = Age; // will be 29 in this example
}
}
Person person = new Person("David", 29);
Well, as others said, the parameterless constructor got executed first, hence your quandary.
I do have to ask however, if you've set a field instead of an automatic property for your Age variable?
public class Person
{
private int _age;
public int Age
{
get { return _age; }
set { _age = value; }
}
}
You could use _age instead of x if that's enough, or if you really need to use x:
public class Person
{
private int _age;
private int x;
public int Age
{
get { return _age; }
set
{
_age = value;
x = _age;
}
}
}
Whichever is more appropriate.
All the examples I see of using the IndexOf() method in List<T> are of basic string types. What I want to know is how to return the index of a list type that is an object, based on one of the object variables.
List<Employee> employeeList = new List<Employee>();
employeeList.Add(new Employee("First","Last",45.00));
I want to find the index where employeeList.LastName == "Something"
int index = employeeList.FindIndex(employee => employee.LastName.Equals(somename, StringComparison.Ordinal));
Edit: Without lambdas for C# 2.0 (the original doesn't use LINQ or any .NET 3+ features, just the lambda syntax in C# 3.0):
int index = employeeList.FindIndex(
delegate(Employee employee)
{
return employee.LastName.Equals(somename, StringComparison.Ordinal);
});
public int FindIndex(Predicate<T> match);
Using lambdas:
employeeList.FindIndex(r => r.LastName.Equals("Something"));
Note:
// Returns:
// The zero-based index of the first occurrence of an element
// that matches the conditions defined by match, if found;
// otherwise, –1.
you can do this through override Equals method
class Employee
{
string _name;
string _last;
double _val;
public Employee(string name, string last, double val)
{
_name = name;
_last = last;
_val = val;
}
public override bool Equals(object obj)
{
Employee e = obj as Employee;
return e._name == _name;
}
}
Sorry, one more for good measure :)
int index = employees.FindIndex(
delegate(Employee employee)
{
return employee.LastName == "Something";
});
Edit: - Full Example in .NET 2.0 Project.
class Program
{
class Employee { public string LastName { get; set; } }
static void Main(string[] args)
{
List<Employee> employeeList = new List<Employee>();
employeeList.Add(new Employee(){LastName="Something"});
employeeList.Add(new Employee(){LastName="Something Else"});
int index = employeeList.FindIndex(delegate(Employee employee)
{ return employee.LastName.Equals("Something"); });
Console.WriteLine("Index:{0}", index);
Console.ReadKey();
}
}
I prefer like this
private List<Person> persons = List<Person>();
public PersonService()
{
persons = new List<Person>() {
new Person { Id = 1, DOB = DateTime.Today, FirstName = "Pawan", LastName = "Shakya" },
new Person { Id = 2, DOB = DateTime.Today, FirstName = "Bibek", LastName = "Pandey" },
new Person { Id = 3, DOB = DateTime.Today, FirstName = "Shrestha", LastName = "Prami" },
new Person { Id = 4, DOB = DateTime.Today, FirstName = "Monika", LastName = "Pandey" },
};
}
public PersonRepository.Interface.Person GetPerson(string lastName)
{
return persons[persons.FindIndex(p=>p.LastName.Equals(lastName, StringComparison.OrdinalIgnoreCase))];
}
The answer is for those coming here to know why IndexOf()
doesn't work.
Your class must override Equals method of object possessing the following declaration.
public override bool Equals(object obj)