This article two purposes, one is the idea of open design, the second is the example code can be used.
The idea of design comes from the first version of "Effective C #" item 24: Use declarative programming instead of imperative programming. In particular, you want to provide a default sort of multiple attributes, not just one attribute, but one that overrides the Icomparable<t> interface that the object property implements, and then calls IComparable to compare without implementing the interface. The sort class implements generics and is type-safe.
The general idea: attribute used to decorate the class we want to get metadata, use reflection to extract metadata, implement some component functions that are independent of object according to the extracted metadata.
So, the effect of this example is to decorate the class object with attribute, set the default sort attribute of the object, and sort it according to these default sorts.
[DefaultSort (New string[] {"ID", "Name"})]
Class Sortdata
{public
int ID {get; set;}
public string Name {get; set;}
public string Value {get; set;}
public override string ToString ()
{return
String.Format ("id:{0},name:{1},value:{2}", ID, Name, Value);
}
For the Sortdata object, we want to sort by its ID, if the ID is equal, and then sort by the Name property. As its name implies, this is the default behavior, we do not need to implement the Sortdata icomparable<sortdata> interface, in the future to change the collation, as long as the change in the DEFAULTSORT of the property name array of content is enough, very convenient.
The Defaultattribute recorded in the original book can only be sorted according to a property name, which is not practical enough to record the names of multiple properties like the following class.
[AttributeUsage (AttributeTargets.Class | Attributetargets.struct, Allowmultiple=false)] public
class DefaultSortAttribute:System.Attribute
{
Private string[] M_propnames;
Public string[] Propnames
{get
{return m_propnames;}
set {m_propnames = value;}
}
Public Defaultsortattribute (String propname)
{
m_propnames = new string[] {propname};
}
Public Defaultsortattribute (string[] propnames)
{
m_propnames = propnames;
}
}
Note that you still have a constructor that you want to sort with only one attribute, and when you decorate a class, it's enough to put [DEFAULTSORT (new string[] {"id", "Name"}), and [DEFAULTSORT ("id")] like that on the class.
Since the use of attribute decorated class, you need to know such metadata, the following needs to use reflection read to the default attribute name to be sorted, compared to the original book, the improvement is to use the generic and precedence properties of the Icomparable<t> interface to compare the sort.
Using System;
Using System.Linq;
Using System.Collections;
Using System.Collections.Generic;
Using System.ComponentModel;
Using System.Reflection;
Namespace Projky.extensions {public class defaultsortcomparer<t>: IComparer, icomparer<t> {
Private ReadOnly propertydescriptor[] m_sortprops;
Private readonly bool M_reverse = FALSE;
Private readonly bool M_valuetype = FALSE;
Public Defaultsortcomparer (): this (false) {} public defaultsortcomparer (bool reverse)
{m_reverse = reverse;
Type T = typeof (T);
M_valuetype = T.isvaluetype;
Object[] A = T.getcustomattributes (typeof (Defaultsortattribute), false); Force check, do not support classes that are not decorated with Defaultsortattribute if (a.length!= 1) throw new NotSupportedException (t.na
ME);
Defaultsortattribute sortname = a[0] as Defaultsortattribute; String[] Propnames = sortname.propnames;
M_sortprops = new Propertydescriptor[propnames.length];
PropertyDescriptorCollection props = typedescriptor.getproperties (t);
for (int i = 0; i < propnames.length i++) {foreach (PropertyDescriptor p in props) {
if (P.name = = Propnames[i]) {m_sortprops[i] = p;
Break
int Icomparer.compare (object left, object right) {
if (hasnull (right) = = true) {int nullcompare = Comparewithnull (left, right); Return m_reverse?
-nullcompare:nullcompare; } if (left. GetType ()!= right.
GetType ()) throw new ArgumentException ("Left and right not match."); if (typeof (T). IsAssignableFrom (left. GetType ()) = = false) throw NEW ArgumentException ("Type not compatible.");
return Compare ((t) left, (t) right); public int Compare (t x, t y) {if (M_valuetype = = False && hasnull (x, y) = = True
{int nullcompare = Comparewithnull (x, y); Return m_reverse?
-nullcompare:nullcompare; foreach (Var prop in m_sortprops) {object xvalue = Prop.
GetValue (x); Object yvalue = Prop.
GetValue (y);
if (Hasnull (Xvalue, yvalue) = = true) {int nullcompare = Comparewithnull (Xvalue, Yvalue); Return m_reverse?
-nullcompare:nullcompare;
Type proptype = Xvalue.gettype (); Priority is given to the Icomaprable<t> interface if (typeof (Icomparable<>). MakeGenericType (Proptype). IsAssignableFrom (Proptype)) {MethodInfo MethodInfo =Proptype.getmethods (). FirstOrDefault (method => method. Name = = "CompareTo" && method. GetParameters (). Length = = 1 && method. GetParameters () [0].
ParameterType = = Proptype);
int gretvalue = (int) methodinfo.invoke (xvalue, new object[] {yvalue});
if (Gretvalue = = 0) continue; Return m_reverse?
-gretvalue:gretvalue;
} IComparable xnongeneric = Xvalue as IComparable;
IComparable ynongeneric = Yvalue as IComparable; if (xnongeneric = null) throw new ArgumentException ("property" + Prop.
Name + "is not comparable.");
int retvalue = Xnongeneric.compareto (Yvalue);
if (RetValue = = 0) continue; Return m_reverse?
-retvalue:retvalue;
return 0; } int ComparewiThnull (object right) {if (left = null) && (right = null)) RET
Urn 0;
if (left = = null) return-1;
return 1;
BOOL Hasnull (object right) {if (left = null | | right = NULL)
return true;
return false; }
}
}