I want an alphabetic sort with one exception.
There is a Group with a Name = "Public" and an ID = "0" that I want first.
(would rather use ID = 0)
After that then sort the rest by Name.
This does not return public first.
public IEnumerable<GroupAuthority> GroupAuthoritysSorted
{
get
{
return GroupAuthoritys.OrderBy(x => x.Group.Name);
}
}
What I want is:
return GroupAuthoritys.Where(x => x.ID == 0)
UNION
GroupAuthoritys.Where(x => x.ID > 0).OrderBy(x => x.Group.Name);
GroupAuthority has a public property Group and Group has Public properties ID and Name.
I used basically the accepted answer
using System.ComponentModel;
namespace SortCustom
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
TestSort();
}
private void TestSort()
{
List<CustomSort> LCS = new List<CustomSort>();
LCS.Add(new CustomSort(5, "sss"));
LCS.Add(new CustomSort(6, "xxx"));
LCS.Add(new CustomSort(4, "xxx"));
LCS.Add(new CustomSort(3, "aaa"));
LCS.Add(new CustomSort(7, "bbb"));
LCS.Add(new CustomSort(0, "pub"));
LCS.Add(new CustomSort(2, "eee"));
LCS.Add(new CustomSort(3, "www"));
foreach (CustomSort cs in LCS) System.Diagnostics.Debug.WriteLine(cs.Name);
LCS.Sort();
foreach (CustomSort cs in LCS) System.Diagnostics.Debug.WriteLine(cs.Name);
}
}
public class CustomSort : Object, INotifyPropertyChanged, IComparable<CustomSort>
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null) PropertyChanged(this, e);
}
private Int16 id;
private string name;
public Int16 ID { get { return id; } }
public String Name { get { return name; } }
public int CompareTo(CustomSort obj)
{
if (this.ID == 0) return -1;
if (obj == null) return 1;
if (obj is CustomSort)
{
CustomSort comp = (CustomSort)obj;
if (comp.ID == 0) return 1;
return string.Compare(this.Name, comp.Name, true);
}
else
{
return 1;
}
}
public override bool Equals(Object obj)
{
// Check for null values and compare run-time types.
if (obj == null) return false;
if (!(obj is CustomSort)) return false;
CustomSort comp = (CustomSort)obj;
return (comp.ID == this.ID);
}
public override int GetHashCode()
{
return (Int32)ID;
}
public CustomSort(Int16 ID, String Name)
{
id = ID;
name = Name;
}
}
}
You need to use a comparison function, they are functions that from two instances of your type return an integer that return 0 if both are equals, a negative value if the first is less than the second and a positive value if the first is greater than the second.
MSDN has a nice table that is easier to follow than text (StackOverflow still doesn't support tables in 2014)
IComparer<T>
Most sort methods accept a custom comparer implementation of type IComparer<T> you should create one encapsulating your custom rules for Group :
class GroupComparer : IComparer<Group>
{
public int Compare(Group a, Group b)
{
if (a != null && b != null && (a.Id == 0 || b.Id == 0))
{
if (a.Id == b.Id)
{
// Mandatory as some sort algorithms require Compare(a, b) and Compare(b, a) to be consistent
return 0;
}
return a.Id == 0 ? -1 : 1;
}
if (a == null || b == null)
{
if (ReferenceEquals(a, b))
{
return 0;
}
return a == null ? -1 : 1;
}
return Comparer<string>.Default.Compare(a.Name, b.Name);
}
}
Usage:
items.OrderBy(_ => _, new GroupAuthorityComparer());
IComparable<T>
If it is the only way to compare Group instances you should make it implement IComparable<T> so that no aditional code is needed if anyone want to sort your class :
class Group : IComparable<Group>
{
...
public int CompareTo(Group b)
{
if (b != null && (Id == 0 || b.Id == 0))
{
if (Id == b.Id)
{
// Mandatory as some sort algorithms require Compare(a, b) and Compare(b, a) to be consistent
return 0;
}
return Id == 0 ? -1 : 1;
}
return Comparer<string>.Default.Compare(Name, b.Name);
}
}
Usage:
items.OrderBy(_ => _.Group);
The choice between one way or the other should be done depending on where this specific comparer is used: Is it the main ordering for this type of item or just the ordering that should be used in one specific case, for example only in some administrative view.
You can even go one level up and provide an IComparable<GroupAuthority> implementation (It's easy once Group implement IComparable<Group>):
class GroupAuthority : IComparable<GroupAuthority>
{
...
public int CompareTo(GroupAuthority b)
{
return Comparer<Group>.Default.Compare(Group, b.Group);
}
}
Usage:
items.OrderBy(_ => _);
The advantage of the last one is that it will be used automatically, so code like: GroupAuthoritys.ToList().Sort() will do the correct thing out of the box.
You can try something like this
list.Sort((x, y) =>
{
if (x.Id == 0)
{
return -1;
}
if (y.Id == 0)
{
return 1;
}
return x.Group.Name.CompareTo(y.Group.Name);
});
Where list is List<T>.
This method takes advantage of custom sort option provided by List<T> using Comparison<T> delegate.
Basically what this method does is, it just adds special condition for comparison when Id, If it is zero it will return a value indicating the object is smaller which makes the object to come in top of the list. If not, it sorts the object using its Group.Name property in ascending order.
public IEnumerable<GroupAuthority> GroupAuthoritysSorted
{
get
{
return GroupAuthoritys.OrderBy(x => x.Group.ID == 0)
.ThenBy(x => x.Group.Name);
}
}
Related
I've taken a stab at writing an incremental source generator; it is generating the correct source code, but it's not doing so incrementally. I feel like it has to be something wrong with my Initialize method or my custom return type (ClassInfo) not being cache friendly. I've never written an IEquatable either, so I really thing it has something to do with that.
ClassInfo
public readonly struct ClassInfo : IEquatable<ClassInfo>
{
public readonly string? Namespace { get; }
public readonly string Name { get; }
public readonly ImmutableArray<IPropertySymbol> PropertyNames { get; }
public ClassInfo(ITypeSymbol type)
{
Namespace = type.ContainingNamespace.IsGlobalNamespace ? null : type.ContainingNamespace.ToDisplayString();
Name = type.Name;
PropertyNames = GetPropertyNames(type);
}
private static ImmutableArray<IPropertySymbol> GetPropertyNames(ITypeSymbol type)
{
return type.GetMembers()
.Select(m =>
{
// Only properties
if (m is not IPropertySymbol prop || m.DeclaredAccessibility != Accessibility.Public)
return null;
// Without ignore attribute
if (GenHelper.IsPropsToStringIgnore(m))
return null;
return (IPropertySymbol)m;
//return SymbolEqualityComparer.Default.Equals(prop.Type, type) ? prop.Name : null;
})
.Where(m => m != null)!
.ToImmutableArray<IPropertySymbol>();
}
public override bool Equals(object? obj) => obj is ClassInfo other && Equals(other);
public bool Equals(ClassInfo other)
{
if (ReferenceEquals(null, other))
return false;
//if (ReferenceEquals(this, other))
// return true;
return Namespace == other.Namespace
&& Name == other.Name
&& PropertyNames.SequenceEqual(other.PropertyNames); // <-- Problem Line
}
public override int GetHashCode()
{
var hashCode = (Namespace != null ? Namespace.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ Name.GetHashCode();
hashCode = (hashCode * 397) ^ PropertyNames.GetHashCode(); // <-- Problem Line
return hashCode;
}
}
IncrementalGenerator.Initialize
public void Initialize(IncrementalGeneratorInitializationContext context)
{
context.RegisterPostInitializationOutput(ctx =>
{
ctx.AddSource("PropsToStringAttribute.g.cs", SourceText.From(AttributeTexts.PropsToStringAttribute, Encoding.UTF8));
ctx.AddSource("PropToStringAttribute.g.cs", SourceText.From(AttributeTexts.PropToStringAttribute, Encoding.UTF8));
ctx.AddSource("PropsToStringIgnoreAttribute.g.cs", SourceText.From(AttributeTexts.PropsToStringIgnoreAttribute, Encoding.UTF8));
});
var classProvider = context.SyntaxProvider
.CreateSyntaxProvider(
static (node, _) => node is ClassDeclarationSyntax { AttributeLists.Count: > 0 },
static (ctx, ct) => GetClassInfoOrNull(ctx, ct)
)
.Where(type => type is not null)
.Collect()
.SelectMany((classes, _) => classes.Distinct());
context.RegisterSourceOutput(classProvider, Generate);
}
GetClassInfoOrNull
internal static ClassInfo? GetClassInfoOrNull(GeneratorSyntaxContext context, CancellationToken cancellationToken)
{
// We know the node is a ClassDeclarationSyntax thanks to IsSyntaxTargetForGeneration
var classDeclarationSyntax = (ClassDeclarationSyntax)context.Node;
var type = ModelExtensions.GetDeclaredSymbol(context.SemanticModel, classDeclarationSyntax, cancellationToken) as ITypeSymbol;
return IsPropsToString(type) ? new ClassInfo(type!) : null;
}
IsPropsToString
public static bool IsPropsToString(ISymbol? type)
{
return type is not null &&
type.GetAttributes()
.Any(a => a.AttributeClass is
{
Name: ClassAttributeName,
ContainingNamespace:
{
Name: PTSNamespace,
ContainingNamespace.IsGlobalNamespace: true
}
});
}
IsPropsToStringIgnore
public static bool IsPropsToStringIgnore(ISymbol type)
{
return type is not null &&
type.GetAttributes()
.Any(a => a.AttributeClass is
{
Name: PropertyIgnoreAttributeName,
ContainingNamespace:
{
Name: PTSNamespace,
ContainingNamespace.IsGlobalNamespace: true
}
});
}
As a side note, I mostly followed this https://www.thinktecture.com/en/net/roslyn-source-generators-performance/
Edit 9/2/22
I have narrowed down the problem to two lines of code noted above in ClassInfo.Equals and ClassInfo.GetHashCode; the two lines that deal with equating the array of names. I commented out those two lines and started to get incremental code generation. However, I wasn't getting new code generation when properties changes (as espected), I instead had to change the name of the class(es) to get new code generated (again, as expected).
Edit 9/7/22
Added project to GitHub
Edit 9/8/22
I tried not using SequenceEquals to compare my PropertyNames array, but it didnt work.
public bool Equals(ClassInfo other)
{
if (PropertyNames.Count() != other.PropertyNames.Count())
return false;
int i = 0;
bool propIsEqual = true;
while (propIsEqual && i < PropertyNames.Count())
{
propIsEqual &= SymbolEqualityComparer.Default.Equals(PropertyNames[i], other.PropertyNames[i]);
i++;
}
return Namespace == other.Namespace
&& Name == other.Name
&& propIsEqual;
//PropertyNames.SequenceEqual(other.PropertyNames); // <-- Problem Line
}
You declared ClassInfo as struct, so it makes no sense to use ReferenceEquals in your Equals implementation. It will box the struct and always return other references.
public bool Equals(ClassInfo other)
{
return Namespace == other.Namespace
&& Name == other.Name
&& PropertyNames.SequenceEqual(other.PropertyNames);
}
PropertyNames.GetHashCode() only gets the hash code of the array object but does not include the array items; however it should, as Equals does it by calling SequenceEqual. The explicit IStructuralEquatable implementation of GetHashCode in ImmutableArray<T> does what we need.
public override int GetHashCode()
{
unchecked {
int hashCode = Namespace != null ? Namespace.GetHashCode() : 0;
hashCode = (hashCode * 397) ^ Name.GetHashCode();
hashCode = (hashCode * 397) ^
((IStructuralEquatable)PropertyNames)
.GetHashCode(EqualityComparer<IPropertySymbol>.Default);
return hashCode;
}
}
The IPropertySymbol implementation(s) must override the Equals and the GetHashCode methods as well. This is also important for SequenceEqual to work as expected.
Edit 22/09/08
We can remove uncertainties about mysterious equality comparers by comparing the properties ourselves:
public bool Equals(ClassInfo other)
{
// Length is faster than the extension method Count()
if (PropertyNames.Length != other.PropertyNames.Length) {
return false;
}
for (int i = 0; i < PropertyNames.Length; i++) {
if (PropertyNames[i].Name != other.PropertyNames[i].Name) {
return false; // We don't need to do the other tests.
}
}
return Namespace == other.Namespace && Name == other.Name;
}
Note: In your test project the "Counter = x" now gets incremented when we change a property name, but not if we add e.g. a comment.
Consequently, we do the same for GetHashCode (see also What is the best algorithm for overriding GetHashCode? and especially Jon Skeet's answer):
public override int GetHashCode()
{
unchecked {
int hash = 17;
hash = hash * 23 + (Namespace ?? "").GetHashCode();
hash = hash * 23 + Name.GetHashCode();
foreach (IPropertySymbol property in PropertyNames) {
hash = hash * 23 + property.Name.GetHashCode();
}
return hash;
}
}
So I created a class like this:
public class ClassName
{
public int ID;
public String n_1 {get; set; }
public String n_2 {get; set; }
// ....
public String n_x {get; set; }
}
Later in my code I compare 2 ClassName objects:
ClassName Item_1 /*...*/ ;
ClassName Item_2 /*...*/ ;
Like this:
if (Item_1 != Item_2 && Item_1.n_a == Item_2.n_a)
{
//do something
}
Now my Problem is that Item_1.ID and Item_2.ID should be ignored. Is there a easy way to do this? The only solution I came up with is a no brainer of like
if ( (Item_1.n_1 != Item_2.n_1 || Item_1.n_2 != Item_2.n_2 || /* ... */ ) && Item_1.n_a == Item_2.n_a)
Override the object.Equals method in your class and specify how you want it to be compared.
Then use !Item1.Equals(Item2) instead of !=
https://learn.microsoft.com/en-us/dotnet/api/system.object.equals?view=netframework-4.7.2
The easy way is what you are actually doing , just create a method like this
public bool Method(ClassName Item_1,ClassName Item2)
{
bool check=false;
if ( (Item_1.n_1 != Item_2.n_1 || Item_1.n_2 != Item_2.n_2 || /* ... */ ) && Item_1.n_a == Item_2.n_a)
check=true
return check
}
To perform something like Item_1 != Item_2 or Item_1 == Item_2 and get all properties (or the ones you want) to be compared, you need to implement your own Equals method and ==, != operators overloads.
== operator, from MSDN:
For reference types other than string, == returns true if its two operands refer to the same object.
For that you can start with some straightforward solution, to implement IEquatable<T> interface, fill your comparing logic inside Equals method and overload == and != operators internally calling your type-safe Equals method:
public class ClassName : IEquatable<ClassName>
{
public int ID;
public String n_1 { get; set; }
public String n_2 { get; set; }
// ....
public String n_x { get; set; }
public static bool operator ==(ClassName obj1, ClassName obj2)
{
if (((object)obj1) == null || ((object)obj2) == null)
return Equals(obj1, obj2);
return obj1.Equals(obj2);
}
public static bool operator != (ClassName obj1, ClassName obj2)
{
if (((object)obj1) == null || ((object)obj2) == null)
return !Equals(obj1, obj2);
return !obj1.Equals(obj2);
}
public bool Equals(ClassName obj)
{
if (obj == null) return false;
return (n_1 == obj.n_1) && (n_2 == obj.n_2) && (n_x == obj.n_x); //you can ignore ID here
}
public override bool Equals(object obj)
{
if (obj == null)
return false;
ClassName classNameObj = obj as ClassName;
if (classNameObj == null)
return false;
else
return Equals(classNameObj);
}
public override int GetHashCode()
{
//This code was generated by VS ide, you can write your own hashing logic
var hashCode = 1032198799;
hashCode = hashCode * -1521134295 + ID.GetHashCode();
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(n_1);
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(n_2);
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(n_x);
return hashCode;
}
}
Then you can check in your if:
if (Item_1 != Item_2)
{
//Do Something
}
For Equals method you should stick to Guidelines for Overriding Equals() and Operator == .
References: == Operator, != Operator, IEquatable Interface, IEquatable.Equals(T) Method, Guidelines for Overriding Equals() and Operator ==
Recently I decided to play around with overriding the Equals method. I used mainly the MSDN guideline (the updated one I believe). So my implementation end up like this:
public class EqualityCheck
{
public int Age { get; set; }
public string Name { get; set; }
public DateTime DateOfBirth { get; set; }
public override bool Equals(object obj)
{
if (null == obj)
{
return false;
}
//If obj is on of the expected type return false
EqualityCheck ec = obj as EqualityCheck;
if (null == ec)
{
return false;
}
//return true if the fields match. This is the place where we can decide what combination should be unique
return (Age == ec.Age) && (Name == ec.Name) && (DateOfBirth == ec.DateOfBirth);
}
public bool Equals(EqualityCheck ec)
{
// If parameter is null return false:
if (ec == null)
{
return false;
}
// Return true if the fields match:
return (Age == ec.Age) && (Name == ec.Name);
}
//How to implement GetHashCode for complex object?
}
And also one child class:
public class EqualityCheckChild : EqualityCheck
{
public int Height { get; set; }
public override bool Equals(System.Object obj)
{
// If parameter cannot be cast to ThreeDPoint return false:
EqualityCheckChild ec1 = obj as EqualityCheckChild;
if (ec1 == null)
{
return false;
}
// Return true if the fields match:
return base.Equals(obj) && Height == ec1.Height;
}
public bool Equals(EqualityCheckChild ec1)
{
// Return true if the fields match:
return base.Equals((EqualityCheck)ec1) && Height == ec1.Height;
}
public static bool operator ==(EqualityCheckChild a, EqualityCheckChild b)
{
if (Equals(a, b))
{
return true;
}
if (a == null || b == null)
{
return false;
}
return a.DateOfBirth == b.DateOfBirth && a.Name == b.Name;
}
public static bool operator !=(EqualityCheckChild a, EqualityCheckChild b)
{
return !(a == b);
}
}
I think this is what MSDN shows as implementation, just with removed casts.
I am using VS2015 and .NET 4.5.2. VS marked the casting as redundant, that's when I look up what's the issue with this. I've read about the possible infinity loop and just out of curiosity I decided to recreate it with the code above. However I can't reproduce it.
Since this is not copy-paste code. At least not literally. I wrote it by hand trying to understand what I am doing so I guess it's possible to have some mismatch with the original code which leads to this. But still my question remains - how to reproduce the problem which is also mentioned in the MSDN article?
Attempt to compare an instance of EqualityCheckChild to null.
EqualityCheckChild foo = new EqualityCheckChild();
Console.WriteLine(foo == null);
This code snippet will cause a StackOverflowException, because in the operator== method, if (a == null || b == null) calls itself.
This is my linq query and I get lots of duplicates with school names.
so I created a regex function to trim the text:
public static string MyTrimmings(string str)
{
return Regex.Replace(str, #"^\s*$\n", string.Empty, RegexOptions.Multiline).TrimEnd();
}
the text gets trimed alright, however, the dropdown values are all duplicates! please help me eliminate duplicates, oh Linq joy!!
ViewBag.schools = new[]{new SelectListItem
{
Value = "",
Text = "All"
}}.Concat(
db.Schools.Where(x => (x.name != null)).OrderBy(o => o.name).ToList().Select(s => new SelectListItem
{
Value = MyTrimmings(s.name),
Text = MyTrimmings(s.name)
}).Distinct()
);
Distinct is poor, GroupBy for the win:
db.Schools.GroupBy(school => school.name).Select(grp => grp.First());
Assuming you have a School class you can write an IEqualityComparer
class SchoolComparer : IEqualityComparer<School>
{
public bool Equals(School x, School y)
{
//Check whether the compared objects reference the same data.
if (Object.ReferenceEquals(x, y)) return true;
//Check whether any of the compared objects is null.
if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
return false;
//Check whether the school' properties are equal.
return x.Name == y.Name;
}
// If Equals() returns true for a pair of objects
// then GetHashCode() must return the same value for these objects.
public int GetHashCode(School school)
{
//Check whether the object is null
if (Object.ReferenceEquals(school, null)) return 0;
//Get hash code for the Name field if it is not null.
int hashSchoolName = school.Name == null ? 0 : school.Name.GetHashCode();
//Calculate the hash code for the school.
return hashSchoolName;
}
}
Then your linq query would look like this:
db.Schools.Where(x => x.name != null)
.OrderBy(o => o.name).ToList()
.Distinct(new SchoolComparer())
.Select(s => new SelectListItem
{
Value = MyTrimmings(s.name),
Text = MyTrimmings(s.name)
});
You could make your class implement the IEquatable<T> interface, so Distinct will know how to compare them. Like this (basic example):
public class SelectListItem : IEquatable<SelectListItem>
{
public string Value { get; set; }
public string Text { get; set; }
public bool Equals(SelectListItem other)
{
if (other == null)
{
return false;
}
return Value == other.Value && Text == other.Text;
}
public override int GetHashCode()
{
unchecked
{
int hash = 17;
if (Value != null)
{
hash = hash * 23 + Value.GetHashCode();
}
if (Text != null)
{
hash = hash * 23 + Text.GetHashCode();
}
return hash;
}
}
}
(GetHashCode taken fron John Skeet's answer here: https://stackoverflow.com/a/263416/249000)
I'm having two lists were one of the lists InboxTemp is automatically is filled with all the objects that exists. The other list NewMessages is randomly (10-60sec) recieving new objects. The problem I have is that I want allList to add the objects in newList without any duplicates.
public List<object> GetNewMessages()
{
if (NewMessages.Count > 0 && InboxTemp.Count > 0)
{
for (int j = 0; j < NewMessages.Count; j++)
{
for (int i = 0; i < InboxTemp.Count; i++)
{
if (InboxTemp[i].ID != NewMessages[j].ID)
{
InboxTemp.Add(NewMessages[j]);
}
}
}
}
NewMessages.Clear();
return InboxTemp;
}
The problem here is that we get duplicates, I just want the new objects to addup with the InboxTemp-list .
var newItems = NewMessages.Where(x => !InboxTemp.Any(z => z.ID == x.ID));
InboxTemp.AddRange(newItems);
If it is possible, please create class implementing IEqualityComparer to compare objects id.
class MyEqualityComparerer : IEqualityComparer<MyMessage>
{
public bool Equals(MyMessage x, MyMessage y)
{
if (x == null && y == null)
{
return true;
}
if (x == null)
{
return false;
}
if (y == null)
{
return false;
}
return x.Id == y.Id;
}
public int GetHashCode(MyMessage obj)
{
if(obj == null)
{
return 0;
}
return obj.Id.GetHashCode();
}
}
Then write get Distinct values from messages and add all not present in the old list to it
var newItems = newList.Distinct(new MyEqualityComparerer()).Except(oldList, new MyEqualityComparerer()).ToList();
oldList.AddRange(newItems);
You could use the modified code based on what you posted:
public List<object> GetNewMessages()
{
if (NewMessages.Count > 0 && InboxTemp.Count > 0)
{
for (int j = 0; j < NewMessages.Count; j++)
{
bool found = false;
int i = 0;
while(!found && i<InboxTemp.Count)
{
if (InboxTemp[i].ID == NewMessages[j].ID)
{
found = true;
}
j++;
}
if(!found)
InboxTemp.Add(NewMessages[j]);
}
}
NewMessages.Clear();
return InboxTemp;
}
Use except:
InboxTemp = InboxTemp.Except(NewMessages);
From MSDN:
Produces the set difference of two sequences by using the default equality comparer to compare values....
...The default equality comparer, Default, is used to compare values of
the types that implement the IEqualityComparer(Of T) generic
interface. To compare a custom data type, you need to implement this
interface and provide your own GetHashCode and Equals methods for the
type.
Source: MSDN Except
You can use Enumerable.Except to find all new messages.
public List<Msg> GetNewMessages()
{
var newMsg = NewMessages.Except(InboxTemp, new MsgComparer()).ToList();
foreach(var msg in newMsg)
InboxTemp.Add(msg);
return newMsg;
}
But you need to create a custom IEqualityComparer<T> for your object (i'll call it Msg):
public class MsgComparer: IEqualityComparer<Msg>
{
public bool Equals(Msg x1, Msg x2)
{
if (object.ReferenceEquals(x1, x2))
return true;
if (x1 == null || x2 == null)
return false;
return x1.ID.Equals(x2.ID);
}
public int GetHashCode(Msg obj)
{
return obj.ID.GetHashCode();
}
}