Matching conditional template of a unique set of objects under ambiguous conditions - c#

It's better to start with code
public interface ICondition<TObject> where TObject : notnull
{
public TObject? Value { get; } //Condition value for primitives
public bool InverseResult { get; init; }
public bool IsSatisfiedBy(TObject obj)
{
return InverseResult ? !Check(obj) : Check(obj);
}
public bool Check(TObject obj);
}
This interface is implemented much in classes like IntegerCondition, BooleanCondition. And other primitives. For example GreaterIntegerCondition, ExactlyIntegerCondition
public interface ITemplate<TObject> : ICondition<TObject> where TObject : notnull
{
TObject? ICondition<TObject>.Value => default;
public IEnumerable<IMappedCondition<TObject>> ConditionsMap { get; }
bool ICondition<TObject>.InverseResult
{
get => false;
init { }
}
public string TemplateName { get; set; }
bool ICondition<TObject>.Check(TObject obj)
{
return ConditionsMap.All(c => c.CheckCondition(obj)) && ConditionsMap.Any();
}
}
This interface extends previous. It can contain many of conditions that mapped to TObject's properties. It is used on more complex objects that can have many properties.
At the end I have this class (simplified version below)
public class FooBunchTemplate
{
public List<ICondition<Foo>> Conditions { get; }
public bool CheckBunch(HashSet<Foo> bunch)
{
var remainingConditions = Conditions.ToList();
foreach(Foo foo in bunch)
{
var matched = remainingConditions.FirstOrDefault(c => c.IsSatisfiedBy(foo));
if(matched == null) return false;
remainingConditions.Remove(matched);
}
return true;
}
}
So, we have set of Foo. Every Foo in HashSet is unique. But problem is the one Foo must match only one condition "slot". There are templates that can be satisfied by different objects (even hundreads of them)
If I just exclude matches, then there might be a situation where the more general condition will be excluded by Foo which would fit also the narrower pattern. Then the CheckBunch() will return false, although if the conditions are set correctly, it should have returned true.
You can think of it as a slot machine. The order is unimportant, but the link 1 condition - 1 object is important.
Example template:
A or B
A
C
Bunch:
CAB
We search C template and exclue 3rd
We search A template and exclude 1st
We search B template but 1st was excluded before.
False is returned
//Wrong result. CAB matches this template, because we can put A in second slot and B in first.
Which approach will be fast and reliable in that situation. Number of conditions in BunchTemplate always fit count of Foo in bunch

Related

C# using list to find elements in class

I'm trying to use List.Contains to find if item in list, has it's bool true or false
Problem is, no matter if the bool is true or false, function always return's false.. her's an example
public class RecepieClass
{
public Recepie recepie;
public bool isUnlocked;
}
public class RecepieList : MonoBehaviour
{
public List<RecepieClass> recepies = new List<RecepieClass>();
public void SelectRecepie(Recepie r)
{
Debug.Log(recepies.Contains(new RecepieClass{recepie = r, isUnlocked = false}));
}
}
Use a for loop, and examine the recipe property of each RecipeClass item. If you know that the Recepie parameter being passed to the function is the actual instance of one of the RecepieClass items in your list, then the default Object.Equals method will work. If it is another instance, you'll need to define your own Equals method (i.e., Recepie should implement IEquatable).
public class RecepieClass
{
public Recepie recepie;
public bool isUnlocked;
}
public class RecepieList : MonoBehaviour
{
public List<RecepieClass> recepies = new List<RecepieClass>();
public void SelectRecepie(Recepie r)
{
foreach (var rc in recepies) {
if (rc.recepie.Equals(r)) {
// you've found it
if (rc.isUnlocked) {
// do something
} else {
// do something else
}
break;
}
}
}
}
If you're into the whole brevity thing, you could use System.Linq.FirstOrDefault (just be prepared for it to return a null).
public void SelectRecepie(Recepie r)
{
if (recepies.FirstOrDefault(rc => rc.recepie.Equals(r))?.isUnlocked ?? false) {
// do something
}
}
When comparing reference types the comparison is determining if objects point to the same object (assuming class doesn't override IEquatable). What you are tying to find is an item with the same values.
The safest way to find an item within a collection is to use a unique identifier within the item combined with the FirstOrDefault() method.
Given there is nothing that seems unique on RecepieClass the example below assumes there is an Id within Recepie referenced from RecepieClass.
var recipe = recepies.FirstOrDefault(r => r.recepie?.Id == 123456);
Debug.Log(recipe);
With the above example recipe will be null or the first RecepieClass with a Recepie matching the supplied id value (123456 in the example).
I dont know if your value r is unique in List or not. i just trap the first found
you just write: (with using System.Linq)
public void SelectRecepie(Recepie r)
{
var result = recepies.FirstOrDefault(x => x.recepie.Equals(r)).isUnlocked;
Debug.Log(result);
}

How to validate the properties of a class hierarchy with a list of property names

I have a class structure like the following
public Class A
{
public B b;
public C c;
public string strA;
}
public Class B
{
public D d;
public string strB;
}
public Class C
{
public string strC1;
public string strC2;
}
public Class D
{
public string strD1;
public string strD2;
}
For an object of class A,
A objA
I need to validate for example :
objA.b.strB
objA.b.D.strD2
objA.c.strC1
that they are nonempty strings. (Of course, it should be validated that the objects objA.b, objA.b.d and objA.c are not null)
I want to realize it with a method like
public bool ValidatePropertiesWithList(object obj, List<string> listOfFieldNamesToValidate, out string nameOfThePropertyThatViolatesTheValidation)
So I want to validate that the properties with the names in listOfFieldNamesToValidate are not empty, and return with an out parameter the name of the property (if any) that violates this.
Should I use Reflection to realize this validation or are Validation Attributes a better choice for me?
Using obj.GetType().GetProperties() seems to be a good place to start but I dont know how I can handle the hierarchical structure of the classes.
Is it possible to mark the properties of the classes with property attributes so that I can get rid of the listOfFieldNamesToValidate parameter in an elegant way?
Using a list of property names or Attributes solves very different problems:
A list of names should be used if its not known at compile time what properties should be validated; the caller is who knows and therefore its he who must supply the needed info.
Using Attributes necessarily means that someone knows at compile time the properties that need validation (this someone is, in the general case, not you; think in a plug-in scenario). Attributes are really handy to manage code scalability reducing dependencies and coupling; changing validation implementations when more classes, properties, validation rules, etc. appear can be very bug prone. Adding a simple Attribute to the new properties is relatively easy and hard to mess up.
Supposing the Attribute path is the one you really want, I've implemented a general case validator that does a couple of nifty things:
It automatically validates all properties marked with the specified Attribute.
It allows you to define validation rules for different property types.
It will not only match exact types, it will also use any valid reference conversions when searching for an applicable rule; for instance an object rule would be applied to a string property if no other rule with a more specific match is found. Note that this will not work with user defined implict conversions; an int rule will not be covered by a long rule and so on. This feature can be disabled.
I haven't tested this extensively but it should work reasonably well:
[AttributeUsage(AttributeTargets.Property)]
public class ValidateAttribute: Attribute
{
}
public class Validator<TAttribute> where TAttribute : Attribute
{
private readonly Dictionary<Type, Predicate<object>> rules;
public Validator()
{
rules = new Dictionary<Type, Predicate<object>>();
}
public bool UnregisterRule(Type t) => rules.Remove(t);
public void RegisterRule<TRule>(Predicate<TRule> rule) => rules.Add(typeof(TRule), o => rule((TRule)o));
public bool Validate<TTarget>(TTarget target, IList<string> failedValidationsBag, bool onlyExactTypeMatchRules = false)
{
var valid = true;
var properties = typeof(TTarget).GetProperties().Where(p => p.GetCustomAttribute<TAttribute>() != null);
foreach (var p in properties)
{
var value = p.GetValue(target);
Predicate<object> predicate = null;
//Rule aplicability works as follows:
//
//1. If the type of the property matches exactly the type of a rule, that rule is chosen and we are finished.
// If no exact match is found and onlyExactMatchRules is true no rule is chosen and we are finished.
//
//2. Build a candidate set as follows: If the type of a rule is assignable from the type of the property,
// add the type of the rule to the candidate set.
//
// 2.1.If the set is empty, no rule is chosen and we are finished.
// 2.2 If the set has only one candidate, the rule with that type is chosen and we're finished.
// 2.3 If the set has two or more candidates, keep the most specific types and remove the rest.
// The most specific types are those that are not assignable from any other type in the candidate set.
// Types are removed from the candidate set until the set either contains one single candidate or no more
// progress is made.
//
// 2.3.1 If the set has only one candidate, the rule with that type is chosen and we're finished.
// 2.3.2 If no more progress is made, we have an ambiguous rules scenario; there is no way to know which rule
// is better so an ArgumentException is thrown (this can happen for example when we have rules for two
// interfaces and an object subject to validation implements them both.)
Type ruleType = null;
if (!rules.TryGetValue(p.PropertyType, out predicate) && !onlyExactTypeMatchRules)
{
var candidateTypes = rules.Keys.Where(k => k.IsAssignableFrom(p.PropertyType)).ToList();
var count = candidateTypes.Count;
if (count > 0)
{
while (count > 1)
{
candidateTypes = candidateTypes.Where(type => candidateTypes.Where(otherType => otherType != type)
.All(otherType => !type.IsAssignableFrom(otherType)))
.ToList();
if (candidateTypes.Count == count)
throw new ArgumentException($"Ambiguous rules while processing {target}: {string.Join(", ", candidateTypes.Select(t => t.Name))}");
count = candidateTypes.Count;
}
ruleType = candidateTypes.Single();
predicate = rules[ruleType];
}
}
valid = checkRule(target, ruleType ?? p.PropertyType, value, predicate, p.Name, failedValidationsBag) && valid;
}
return valid;
}
private bool checkRule<T>(T target, Type ruleType, object value, Predicate<object> predicate, string propertyName, IList<string> failedValidationsBag)
{
if (predicate != null && !predicate(value))
{
failedValidationsBag.Add($"{target}: {propertyName} failed validation. [Rule: {ruleType.Name}]");
return false;
}
return true;
}
}
And you'd use it as follows:
public class Bar
{
public Bar(int value)
{
Value = value;
}
[Validate]
public int Value { get; }
}
public class Foo
{
public Foo(string someString, ArgumentException someArgumentExcpetion, Exception someException, object someObject, Bar someBar)
{
SomeString = someString;
SomeArgumentException = someArgumentExcpetion;
SomeException = someException;
SomeObject = someObject;
SomeBar = someBar;
}
[Validate]
public string SomeString { get; }
[Validate]
public ArgumentException SomeArgumentException { get; }
[Validate]
public Exception SomeException { get; }
[Validate]
public object SomeObject { get; }
[Validate]
public Bar SomeBar { get; }
}
static class Program
{
static void Main(string[] args)
{
var someObject = new object();
var someArgumentException = new ArgumentException();
var someException = new Exception();
var foo = new Foo("", someArgumentException, someException, someObject, new Bar(-1));
var validator = new Validator<ValidateAttribute>();
var bag = new List<string>();
validator.RegisterRule<string>(s => !string.IsNullOrWhiteSpace(s));
validator.RegisterRule<Exception>(exc => exc == someException);
validator.RegisterRule<object>(obj => obj == someObject);
validator.RegisterRule<int>(i => i > 0);
validator.RegisterRule<Bar>(b => validator.Validate(b, bag));
var valid = validator.Validate(foo, bag);
}
}
And of course, bag's content is the expected:
Foo: SomeString failed validation. [Rule: String]
Foo: SomeArgumentException failed validation. [Rule: Exception]
Bar: Value failed validation. [Rule: Int32]
Foo: SomeBar failed validation. [Rule: Bar]

Setting up a simple iequatable class c#

Cant find a simple answer. My problem is I am trying to compare the VALUE of an object in a list to the VALUE of an object...
my class:
public class MatchList
{
public int SomeInt { get; set; }
public decimal SomeDecimal { get; set; }
}
I create theMatchList. It seems that I can only compare the object and not the values for object with 'theMatchList.Contains...'
MatchList ML = new MatchList();
ML.SomeInt = 12;
ML.SomeDecimal = 2.3;
if (theMatchlist.Contains(ML))
{
DoSomething;
}
How do get to fire 'DoSomething'? Assuming that there is an entry in 'theMatchList' where the values equal 12 and 2.3 respectively. I know it has something to do with iequatable, but I dont quite understand how that works. Thanks in advance!
Your naming is a bit unclear, I assume that you actually have a List<MatchList> that you want to find a particular MatchList in (I suggest renaming MatchList to at least MatchItem in that case and preferable something more descriptive).
Then from the documentation of List<T>.Contains:
This method determines equality by using the default equality comparer, as defined by the object's implementation of the IEquatable<T>.Equals method for T (the type of values in the list).
So you will have to implement IEquatable<T> for your class. In addition, the advice is that
[i]f you implement Equals, you should also override the base class implementations of Object.Equals(Object) and GetHashCode so that their behavior is consistent with that of the IEquatable.Equals method.
If you implement GetHashCode, its result should not change over the lifetime of your object. In most cases, making the class immutable is sufficient. If you need to be able to update the fields, you need to implement GetHashCode differently.
So all in all, if you want to use Contains your class will end up looking something like below:
public class MatchList : IEquatable<MatchList>
{
// Note: Fields are readonly to satisfy GetHashCode contract
private readonly int someInt;
private readonly decimal someDecimal;
// Public constructor creates immutable object
public MatchList(int myInt, decimal myDecimal)
{
this.someInt = myInt;
this.myDecimal = myDecimal;
}
// Properties are now read-only too.
public int SomeInt { get { return this.someInt; } }
public decimal SomeDecimal { get { return this.someDecimal; } }
// Implementation of IEquatable<MatchList>
public bool Equals( MatchList other )
{
return (other != null)
&& (this.SomeInt == other.SomeInt)
&& (this.SomeDecimal == other.SomeDecimal);
}
// Override of Object.Equals
// Calls the IEquatable.Equals version if possible.
public override bool Equals( object obj )
{
return (obj is MatchList) && this.Equals(obj as MatchList);
}
public override int GetHashCode()
{
return (this.someInt * 17) ^ this.someDecimal.GetHashCode();
}
}
As I commented, your question is pretty unclear so I'll do my best to explain the concept.
It's pretty likely what you were trying to code is the items in the list not the list itself:
public class MatchItem : IEquatable<MatchItem>
{
public int SomeInt { get; set; }
public decimal SomeDecimal {get; set; }
public bool Equals(MatchItem item)
{
if(item == null)
return false;
return this.SomeInt == item.SomeInt && this.SomeDecimal == item.SomeDecimal;
}
// You should also override object.ToString, object.Equals & object.GetHashCode.
// Omitted for brevity here!
}
You'll note that has an implementation of IEquatable<MatchItem> which allows it to be compared to other instances of MatchItem.
Thereafter, this code will work:
var items = new List<MatchItem>()
{
new MatchItem{SomeInt = 1, SomeDecimal = 0.3M},
new MatchItem{SomeInt = 12, SomeDecimal = 2.3M}
};
var searchItem = new MatchItem{SomeInt = 1, SomeDecimal = 0.3M};
Console.WriteLine(items.Contains(searchItem)); // true
Working example: http://rextester.com/ZWNC6890

Removing duplicates from a list<int,int> [duplicate]

I have to distinct list of object but NOT only by ID because sometimes two different objects have same ID.
I have class:
public class MessageDTO
{
public MessageDTO(MessageDTO a)
{
this.MsgID = a.MsgID;
this.Subject = a.Subject;
this.MessageText = a.MessageText;
this.ViewedDate = a.ViewedDate;
this.CreatedDate = a.CreatedDate;
}
public int? MsgID { get; set; }
public string Subject { get; set; }
public string MessageText { get; set; }
public System.DateTime? ViewedDate { get; set; }
public System.DateTime? CreatedDate { get; set; }
}
How I can distinct list of:
List<MessageDTO> example;
Thanks
Use LINQ.
public class MessageDTOEqualityComparer : EqualityComparer<MessageDTO>
{
public bool Equals(MessageDTO a, MessageDTO b)
{
// your logic, which checks each messages properties for whatever
// grounds you need to deem them "equal." In your case, it sounds like
// this will just be a matter of iterating through each property with an
// if-not-equal-return-false block, then returning true at the end
}
public int GetHashCode(MessageDTO message)
{
// your logic, I'd probably just return the message ID if you can,
// assuming that doesn't overlap too much and that it does
// have to be equal on the two
}
}
Then
return nonDistinct.Distinct(new MessageDTOEqualityComparer());
You can also avoid the need for an extra class by overriding object.Equals(object) and object.GetHashCode() and calling the empty overload of nonDistinct.Distinct(). Make sure you recognize the implications of this decision, though: for instance, those will then become the equality-testing functions in all non-explicit scopes of their use. This might be perfect and exactly what you need, or it could lead to some unexpected consequences. Just make sure you know what you're getting into.
I you want to use other properties, you should implement IEqualityComparer interface. More on: msdn
class MsgComparer : IEqualityComparer<MessageDTO>
{
public bool Equals(MessageDTO x, MessageDTO Oy)
{
}
// If Equals() returns true for a pair of objects
// then GetHashCode() must return the same value for these objects.
public int GetHashCode(MessageDTO m)
{
//it must br overwritten also
}
}
Then:
example.Distinct(new MsgComparer());
You could also overwrite Equals in MessageDTO class:
class MessageDTO
{
// rest of members
public override bool Equals(object obj)
{
// your stuff. See: http://msdn.microsoft.com/en-us/library/ms173147%28v=vs.80%29.aspx
}
public override int GetHashCode()
{
}
}
Then it's enough:
example.Distinct();
You could use the extension method DistinctBy from the MoreLinq library:
string[] source = { "first", "second", "third", "fourth", "fifth" };
var distinct = source.DistinctBy(word => word.Length);
See here:
I recommend you using solution of #Matthew Haugen
In case you don't want to create a new class for that, there is a way to use LINQ by grouping you list by distinct field(s) then select the first item on this group. For example:
example.(e => new { e.MsgID, e.Subject }).Select(grp => grp.FirstOrDefault());

C# get if a property is any sort of list

I'm trying to make a text writer according to my classes properties in the following pattern:
MyClass
ID 1
Name MyName
AnotherProperty SomeValue
ThisIsAnotherClass
AnotherClassProperties 1
//Example class
public class MyClass
{
public int ID { get; set; }
public string Name { get; set; }
public string AnotherProperty { get; set; }
public AnotherClass ThisIsAnotherClass { get; set; }
}
So I'm taking each property name, writing it, a blank space, then it's value (if there is any).
Now I'm trying to implement support for lists and anything array-like for something like this:
MyClass
ArrayTest
1
2
3
If it's a class, I'll have a recursive for the function so I can display all values inside the list/array in this pattern. (it's for a webservice)
My question is, how can I find if a specific property is something list-able?
I've tried:
Type type = myObject.GetType();
PropertyInfo[] properties = type.GetProperties();
for(int i = 0; i < properties.Length; i++)
{
if(properties[i].PropertyType.IsGeneric) //Possible List/Collection/Dictionary
{
//Here is my issue
Type subType = properties[i].PropertyType.GetGenericTypeDefinition();
bool isAssignable = subType.IsAssignableFrom(typeof(ICollection<>)); //Always false
bool isSubclass = subType.IsSubclassOf(typeof(ICollection<>)); //Always false
//How can I figure if it inherits ICollection/IEnumerable so I can use it's interface to loop through it's elements?
}
else if(properties[i].PropertyType.IsArray) //Array
{
}
else if(properties[i].PropertyType.IsClass && !properties[i].PropertyType.Equals(typeof(String)))
{
//Non-string Subclasses, recursive here
}
else
{
//Value types, write the text + value
}
}
Like mentioned in the comments: use Json as a way to format objects, it will save a lot of time.
If you have reason not to do this, you can check if the type is enumerable: This also covers the Type.IsArray case.
typeof(IEnumerable).IsAssignableFrom(properties[i].PropertyType)
As an added notice of caution: maybe you do not want to enumerate String and byte[] type objects.

Categories

Resources