Hi,
I've a class which has a method with some condition checks and append a string according to condition. I tried to refactor this code with abstract factory pattern.
My problem is related to default expression class. If none of other rules applies to related filter, then I want to apply default expression rule. However on my application; it depends the order of StandartExpression instance order in the _expressions list. If it is added first then the other rules will not be checked.
I think I missed something about that pattern. Could you help me?
This is my application code:
public class Filter
{
private string _operator = "="; // =, !=, <,> ...
public string Name { get; set; }
public object Value { get; set; }
public string Operator { get { return _operator; } set { _operator = value; } }
}
Filter filter = null;
StringBuilder whereClause = new StringBuilder();
for (int i = 0; i < array.Length; i++)
{
filter = array[i];
if (filter.Value.ToString().ToLower().Equals("null"))
{
whereClause.Append(filter.Name + " " + filter.Operator + " null ");
}
else if (filter.Operator.ToLower().Equals("like"))
{
whereClause.Append(filter.Name + ".Contains(#" + i + ")");
}
else
{
whereClause.Append(filter.Name + " " + filter.Operator + " #" + i);
}
whereClause.Append(" AND ");
}
Now, this is the abstract factory pattern code:
public interface ICustomExpression
{
bool ExpressionIsValid(Filter filter);
string GetExpressionStr(Filter filter, int index);
}
public class StandardExpression : ICustomExpression
{
public bool ExpressionIsValid(Filter filter)
{
return true;
}
public string GetExpressionStr(Filter filter, int index)
{
return filter.Name + " " + filter.Operator + " #" + index;
}
}
public class LikeExpression : ICustomExpression
{
public bool ExpressionIsValid(Filter filter)
{
return filter.Operator.ToLower().Equals("like");
}
public string GetExpressionStr(Filter filter, int index)
{
return filter.Name + ".Contains(#" + index + ")";
}
}
public class NullExpression : ICustomExpression
{
public bool ExpressionIsValid(Filter filter)
{
return filter.Value.ToString().ToLower().Equals("null");
}
public string GetExpressionStr(Filter filter, int index)
{
return filter.Name + " " + filter.Operator + " null ";
}
}
public static class ExpressionFactory
{
private static List<ICustomExpression> _expressions = null;
public static List<ICustomExpression> Expressions
{
get
{
if (_expressions == null)
{
Build();
}
return _expressions;
}
}
private static void Build()
{
_expressions = new List<ICustomExpression>();
_expressions.Add(new NullExpression());
_expressions.Add(new LikeExpression());
_expressions.Add(new StandardExpression());
}
}
And that is how I used that structure on my application code:
StringBuilder whereClause = new StringBuilder();
Filter filter = null;
var array = filterList.ToArray();
for (int i = 0; i < array.Length; i++)
{
filter = array[i];
foreach (ICustomExpression exp in ExpressionFactory.Expressions)
{
if (exp.ExpressionIsValid(filter))
whereClause.Append(exp.GetExpressionStr(filter, i));
}
whereClause.Append(" AND ");
}
Your feeling is right: there is a hidden distinction between "special" and "standard" expressions that should be made explicit for clarity.
However, in my eyes the bigger problem is that your factory is not actually handling the type selection and instantiation on its own but is delegating parts of that responsibility to its clients. Every client has to know how to work with the collection property Expressions and thus also has to know about this implicit distinction I mentioned above.
Ideally clients of the ExpressionFactory should just hand it a filter in order to get the correct ICustomExpression instance. Here's how this might look like:
public static class ExpressionFactory
{
private static StandardExpression _standardExpression = new StandardExpression();
private static ICustomExpression[] _specialExpressions = new []
{
(ICustomExpression)new NullExpression(),
(ICustomExpression)new LikeExpression()
};
public static ICustomExpression GetBy(Filter filter)
{
var match = _specialExpressions.SingleOrDefault(e => e.ExpressionIsValid(filter));
if (match == null)
return _standardExpression;
return match;
}
}
The rules how an expression is created (or rather selected, in your case) is now only known by the factory and thus implemented in a single place.
I also made the implicit distinction between "special" and "standard" expressions explicit, so the implementation does not depend on the order in which the ICustomExpressions are added anymore.
Note that the usage of the factory now becomes more straight-forward:
for (int i = 0; i < array.Length; i++)
{
filter = array[i];
var exp = ExpressionFactory.GetBy(filter);
whereClause.Append(exp.GetExpressionStr(filter, i));
whereClause.Append(" AND ");
}
Here is a working example of the refactored code: https://dotnetfiddle.net/uzVJhM
By the way: your implementation looks more like an instance of the factory method pattern.
Related
I am pretty sure that my problem comes from a really stupid mistake but I can not find it...
I am using a custom class as the key in a SortedList(I also tried SortedDictionary).
The first item gets added without a problem but when I try to add a second Item ContainsKey() returns true.
The Class I am using as a Key overrrides Equals() and GetHashCode().
I checked what items actually get compared and that's what I found out:
Manually calling Equals() to compare two items works just fine but when it gets called via ContainsKey the object compares itself with the same or another instance of itself. I made sure to check if the object to be added is indeed a new one and it is...
This is the Key-Class
using System;
using System.Collections;
using System.Collections.Generic;
using TFVR.Components.Gaia.Content.Element;
using UnityEngine;
namespace TFVR.Components.Gaia.Content.QueueUi
{
public class QueueEntryElement : IComparable
{
public string Created;
public string ProductId;
public ContactInformationElement ContactInformation;
public int Priority;
public string OrderId;
public QueueEntryElement(string created, string productId
, ContactInformationElement contactInformation, int priority
, string orderId)
{
Created = created;
ProductId = productId;
ContactInformation = contactInformation;
Priority = priority;
OrderId = orderId;
}
public int CompareTo(object obj)
{
if (obj == null) return 1;
QueueEntryElement otherQueueEntryElement = obj as QueueEntryElement;
if (otherQueueEntryElement != null)
return this.Priority.CompareTo(otherQueueEntryElement.Priority);
else
throw new ArgumentException("Object is not a QueueEntryElement");
}
public override bool Equals(object obj)
{
if ((obj == null) || !this.GetType().Equals(obj.GetType()))
{
return false;
}
else
{
QueueEntryElement e = (QueueEntryElement)obj;
return (this.OrderId == e.OrderId);
}
}
public override int GetHashCode()
{
return OrderId.GetHashCode();
}
public override string ToString()
{
string str = "Created: "
+ Created + ", "
+ "Product Id: "
+ ProductId + ", "
+ "Contact Information: "
+ "{" + ContactInformation + "}" + ", "
+ "Priority: "
+ Priority + ", "
+ "Order Id: "
+ OrderId;
return str;
}
}
}
This is the code where I am trying to add to the SortedList
SortedList<QueueEntryElement, string> dict = new SortedList<QueueEntryElement, string>();
private void add(QueueEntryElement q, string
{
if (!dict.ContainsKey(q))
{
dict.Add(q, s);
}
}
ContactInformationElement c1 = new ContactInformationElement("a","b","c","d","e");
QueueEntryElement e1 = new QueueEntryElement("a","b", c1, 0,"123");
ContactInformationElement c2 = new ContactInformationElement("f", "g", "h", "i", "j");
QueueEntryElement e2 = new QueueEntryElement("c", "d", c2, 0, "234");
add(e1,"one");
add(e2,"two");
The problem here is that SortedList.ContainsKey uses CompareTo... NOT Equals to determine existence.
This means that you are basically using Priority as the Key NOT OrderId.
So for the purpose of your example the actual key is Priority.
So if your items do not have unique priority values then they will not be added to the "dictionary".
This is the normal behavior of the C# Generic SortedList.
I added some dummy code just in case anyone is still interested in testing. To solve my problem i simply changed my CompareTo() to this:
public int CompareTo(QueueEntryElement obj)
{
if (obj == null) return 1;
QueueEntryElement otherQueueEntryElement = obj as QueueEntryElement;
if (otherQueueEntryElement != null)
{
if (Priority.CompareTo(otherQueueEntryElement.Priority) == 0)
{
return OrderId.CompareTo(otherQueueEntryElement.OrderId);
}
return 0;
}
else
throw new ArgumentException("Object is not a QueueEntryElement");
}
C# code:
using System;
// this is the delegate declaration
public delegate int Comparer(object obj1, object obj2);
public class Name
{
public string FirstName = null;
public string LastName = null;
public Name(string first, string last)
{
FirstName = first;
LastName = last;
}
// this is the delegate method handler
public static int CompareFirstNames(object name1, object name2)
{
string n1 = ((Name)name1).FirstName;
string n2 = ((Name)name2).FirstName;
if (String.Compare(n1, n2) > 0)
{
return 1;
}
else if (String.Compare(n1, n2) < 0)
{
return -1;
}
else
{
return 0;
}
}
public override string ToString()
{
return FirstName + " " + LastName;
}
}
class SimpleDelegate
{
Name[] names = new Name[5];
public SimpleDelegate()
{
names[0] = new Name("Joe", "Mayo");
names[1] = new Name("John", "Hancock");
names[2] = new Name("Jane", "Doe");
names[3] = new Name("John", "Doe");
names[4] = new Name("Jack", "Smith");
}
static void Main(string[] args)
{
SimpleDelegate sd = new SimpleDelegate();
// this is the delegate instantiation
Comparer cmp = new Comparer(Name.CompareFirstNames);
Console.WriteLine("\nBefore Sort: \n");
sd.PrintNames();
// observe the delegate argument
sd.Sort(cmp);
Console.WriteLine("\nAfter Sort: \n");
sd.PrintNames();
}
// observe the delegate parameter
public void Sort(Comparer compare)
{
object temp;
for (int i=0; i < names.Length; i++)
{
for (int j=i; j < names.Length; j++)
{
// using delegate "compare" just like
// a normal method
if ( compare(names[i], names[j]) > 0 )
{
temp = names[i];
names[i] = names[j];
names[j] = (Name)temp;
}
}
}
}
public void PrintNames()
{
Console.WriteLine("Names: \n");
foreach (Name name in names)
{
Console.WriteLine(name.ToString());
}
}
}
There are two parts of the code that I don't understand at all. What do these parts exactly do, (respectively); and what subject(s) do I need to learn to understand these parts?
string n1 = ((Name)name1).FirstName;
What does ((Name)name1) do?
public void Sort(Comparer compare)
Can anyone provide any tips or feedback on how those parts of the code function? Can you possibly lead me into the right direction?
First: string n1 = ((Name)name1).FirstName
(Name)name1 which can be written generically as (Type)variable. What this does is take the variable and attempt to cast (a synonym would be convert) the variable name1 into the type Name. This then allows us to access the FirstName property.
Learn more here: https://www.w3schools.com/cs/cs_type_casting.asp
Second: public void Sort(Comparer compare)
This is a method declaration.
public indicates the accessibility. Other options include private, protected and a few others. public indicates that this method can be called by any code that has a reference to an instance of this object.
void indicates the return type. void is a special keyword to mean that no value is returned from the method.
Sort is the method name
Compare compare: Compare is the type of the parameter to be passed in and compare is the name of the parameter which can be used throughout the method.
Learn more here: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/methods
I have the following property Class:
public class Ctas
{
private string _CodAgrup;
public string CodAgrup
{
get { return _CodAgrup; }
set { _CodAgrup = value; }
}
private string _NumCta;
public string NumCta
{
get { return _NumCta; }
set { _NumCta = value; }
}
private string _Desc;
public string Desc
{
get { return _Desc; }
set { _Desc = value; }
}
private string _subctade;
public string SubCtaDe
{
get { return _subctade; }
set { _subctade = value; }
}
private string _Nivel;
public string Nivel
{
get { return _Nivel; }
set { _Nivel = value; }
}
private string _Natur;
public string Natur
{
get { return _Natur; }
set { _Natur = value; }
}
public override string ToString()
{
return "CodAgrup = " + CodAgrup + ", NumCta = " + NumCta + ", Desc = " + Desc + ", SubCtaDe = " + SubCtaDe + ", Nivel = " + Nivel + ", Natur = " + Natur;
}
#endregion
}
and I have Create an XML from these properties, so first I have to fill the properties, then i got the next method i want to use to fill the properties, first question is, is it correct the way Im using to fill the properties?
Then I should retreive the data and write it on an XML file so I convert properties data into a list and then just write them as atributes but when i Debug, I get that the list is empty, Why is that? what could be the best way to do it?
//Insert n data on properties
static void cuenta(string codagroup, string numcta, string desc, string subctade, string nivel, string natur)
{
Ctas cuentas = new Ctas();
int x = 0;
while (cuentas.CodAgrup != null)
{
cuentas.CodAgrup.Insert(x, "codagroup");
cuentas.NumCta.Insert(x, "numcta");
cuentas.Desc.Insert(x, "desc");
cuentas.SubCtaDe.Insert(x,"subctade");
cuentas.Nivel.Insert(x, "nivel");
cuentas.Natur.Insert(x, "natur");
x = x + 1;
}
}
//Converting propierties data into list
List<string> coda = cuentas.CodAgrup.GetType().GetProperties().Select(p => p.Name).ToList();
List<string> ncta = cuentas.NumCta.GetType().GetProperties().Select(p => p.Name).ToList();
List<string> desc = cuentas.Desc.GetType().GetProperties().Select(p => p.Name).ToList();
List<string> subdes = cuentas.SubCtaDe.GetType().GetProperties().Select(p => p.Name).ToList();
List<string> nivel = cuentas.Nivel.GetType().GetProperties().Select(p => p.Name).ToList();
List<string> natur = cuentas.Natur.GetType().GetProperties().Select(p => p.Name).ToList();
//Create XML from data in list´s
for (int i = 0; i < coda.Count; i++)
{
xmlWriter.WriteAttributeString("CodAgrup", coda[i]);
xmlWriter.WriteAttributeString("NumCta", ncta[i]);
xmlWriter.WriteAttributeString("Desc", desc[i]);
//write the atribute when property data exists.
if (cuentas.SubCtaDe != null)
{
xmlWriter.WriteAttributeString("SubCtaDe", subdes[i]);
}
xmlWriter.WriteAttributeString("Nivel", nivel[i]);
xmlWriter.WriteAttributeString("Natur", natur[i]);
xmlWriter.WriteEndElement();
}
Your code is confusing, but if I understand it right, here is the first error I see:
Ctas cuentas = new Ctas();
int x = 0;
while (cuentas.CodAgrup != null) // cuentas.CodAgrup is null from the beginning!
{
cuentas.CodAgrup.Insert(x, "codagroup");
cuentas.NumCta.Insert(x, "numcta");
cuentas.Desc.Insert(x, "desc");
cuentas.SubCtaDe.Insert(x,"subctade");
cuentas.Nivel.Insert(x, "nivel");
cuentas.Natur.Insert(x, "natur");
x = x + 1;
}
Since you are looking at a brand-new Ctas object, and there is no code to initialize the CodAgrup property, it will have the default value of null, so the code never enters the while loop.
Even if it DID, I suspect it would be an endless loop, because you're Inserting a literal value into a string property, and there is no condition I see where cuentas.CodAgrup will ever be null.
As for your XML generation, why not just use the built in XmlSerializer class? Even if you require a specific format, there are attributes that let you customize the XML that is generated.
I have a list with 1000000 items and I need to figure out if a item is inside but by reference. Therefore I can't use Contains since Contains doesn't always match by reference (e.g. when list of type string). I tried list.Any(x => object.ReferenceEquals) but that is too slow.
Take a look here:
for(int i = 0; i < 1000000; i++)
{
if(does list contains this item anotherList[i])
{
list.Add(anotherList[i]);
}
}
How do I perform this really fast?
Use a dictionary with an IdendityEqualityComparer to get the key comparison in the dictionary to do a reference comparison. The main difference between this approach and yours is that you have an O(1) lookup, instead of an O(n) lookup that you get from having to go through an entire list for each item.
Put the following code inside a sample Console app project; it basically splits a master dictionary into two.
public class IdentityEqualityComparer<T> : IEqualityComparer<T> where T : class
{
public int GetHashCode(T value)
{
return RuntimeHelpers.GetHashCode(value);
}
public bool Equals(T left, T right)
{
return left == right; // Reference identity comparison
}
}
public class RefKeyType
{
public int ID { get; set; }
}
class Program
{
public static void Main()
{
var refDictionary = new Dictionary<RefKeyType, int>(1000000, new IdentityEqualityComparer<RefKeyType>());
var testDictionary = new Dictionary<RefKeyType, int>(1000000, new IdentityEqualityComparer<RefKeyType>());
var store = new Dictionary<RefKeyType, int>(1000000);
for (var i = 0; i < 1000000; i++)
{
var key = new RefKeyType() {ID = i};
refDictionary[key] = i;
//Load the test dictionary if I is divisible by 2
if (i%2 == 0)
{
testDictionary[key] = i;
}
}
foreach (var key in refDictionary.Keys)
{
int val;
if (!testDictionary.TryGetValue(key, out val))
{
store[key] = val;
}
}
Console.WriteLine("Master dictionary has " + refDictionary.Count);
Console.WriteLine("Test dictionary has " + testDictionary.Count);
Console.WriteLine("Store dictionary has " + store.Count);
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
I am relatively new to C# (WinForms), and had a question regarding combo boxes. I have a combo box of Reviewer objects (it is a custom class with an overridden ToString method) and am currently attempting to go through all the checked items and use them to generate a setup file.
Here is how the combo box is populated (populated on form load). Parameters is just a collection of linked lists and parsing code.
for (int i = 0; i < parameters.GetUsers().Count; i++)
{
UserList.Items.Add(parameters.GetUsersArray()[i], parameters.GetUsersArray()[i].isSelected());
}
Here is how I am trying to read it. setup is a StringBuilder. The problem is that GetID is not defined. Does the add function above cast the Reviewer object to a Object object? It looks a little funny since it creates a file fed into a Perl script. A sample desired output line looks like this: inspector0 => "chg0306",
for (int i = 0; i < UserList.CheckedItems.Count; i++)
{
setup.AppendLine("inspector" + i.ToString() + " => \t \"" +
UserList.CheckedItems[i].GetID() + "\",");
}
Here is the users class: (Sample User is ID = aaa0000 name: Bob Joe)
public class Reviewer
{
private string name;
private string id;
private bool selected;
public Reviewer(string newName, string newID, bool newSelected)
{
name = newName;
id = newID;
selected = newSelected;
}
public string GetName()
{
return name;
}
public override string ToString()
{
//string retVal = new string(' ', id.Length + name.Length + 1);
string retVal = id + '\t' + name;
return retVal;
}
public string GetID()
{
return id;
}
public bool isSelected()
{
return selected;
}
}
For posterity, here is the Parameters class:
public class ParameterLists
{
public ParameterLists()
{
projects = new LinkedList<string>();
reviewers = new LinkedList<Reviewer>();
}
public enum FileContents {
PROJECT_LIST,
USERS_LIST,
}
public LinkedList<Reviewer> GetUsers()
{
return reviewers;
}
public LinkedList<string> GetProjects()
{
return projects;
}
public Reviewer[] GetUsersArray()
{
Reviewer[] userArray = new Reviewer[reviewers.Count];
reviewers.CopyTo(userArray, 0);
return userArray;
}
public string[] GetProjectsArray()
{
String[] projectArray = new String[projects.Count];
projects.CopyTo(projectArray, 0);
return projectArray;
}
public void LoadParameters(string fileName)
{
//Reads the parameters from the input file.
}
private void CreateDefaultFile(string fileName)
{
// Create the file from the defaultfile , if it exists.
// Otherwise create a blank default file.
}
private LinkedList <string> projects;
private LinkedList <Reviewer> reviewers;
}
I am probably missing something simple, coming from embedded C++. Any help would be appreciated.
You have to cast that object:
((Reviewer)UserList.CheckedItems[i]).GetID()