c#class実装()(良好)
18784 ワード
c#class実装()(良好)
Introduction
While implementing my first projects using C# I found out that there were several issues to take into account if I wanted my classes to behave correctly and make good friends with .NET. This list is more about the "hows"and the "whats"and not the "whys"and while it is in no way complete, it contains the guidelines that I currently follow. Ah, and I almost forgot... it's guaranteed to be incomplete!
The Guidelines
IComparable.CompareTo()
if ordering of objects makes sense. Also implement operators <
, <=
, >
and >=
in terms of IComparable.CompareTo()
. int IComparable.CompareTo(object obj)
{
return m_data - ((MyType)obj).m_data;
}
publicstaticbooloperator<(MyType lhs, MyType rhs)
{
return ((IComparable)lhs).CompareTo(rhs) < 0;
}
publicstaticbooloperator<=(MyType lhs, MyType rhs)
{
return ((IComparable)lhs).CompareTo(rhs) <= 0;
}
publicstaticbooloperator>(MyType lhs, MyType rhs)
{
return ((IComparable)lhs).CompareTo(rhs) > 0;
}
publicstaticbooloperator>=(MyType lhs, MyType rhs)
{
return ((IComparable)lhs).CompareTo(rhs) >= 0;
}
private MyType(int data)
{
m_data = data;
}
publicstaticexplicitoperatorMyType(int from)
{
returnnew MyType(from);
}
publicstaticimplicitoperatorint(MyType from)
{
return from.m_data;
}
Object.ToString()
to return a significant textual representation. publicoverridestring ToString()
{
returnstring.Format("MyType: {0}", m_data);
}
Object.GetHashCode()
and Object.Equals()
if object equality makes sense. If two objects are equal ( Object.Equals()
returns true) they should return the same hash code and this value should be immutable during the whole lifecycle of the object. The primary key is usually a good hash code for database objects.For reference types implement operators ==
and !=
in terms of Object.Equals()
. publicoverrideintGetHashCode()
{
return m_data.GetHashCode();
}
publicoverrideboolEquals(object obj)
{
// Call base.Equals() only if this class derives from a
// class that overrides Equals()
if(!base.Equals(obj))
returnfalse;
if(obj == null)
returnfalse;
// Make sure the cast that follows won't failif(this.GetType() != obj.GetType())
returnfalse;
// Call this if m_data is a value type
MyType rhs = (MyType) obj;
return m_data.Equals(rhs.m_data);
// Call this if m_data is a reference type//return Object.Equals(m_data, rhs.m_data);
}
publicstaticbooloperator==(MyType lhs, MyType rhs)
{
if(lhs == null)
returnfalse;
return lhs.Equals(rhs);
}
publicstaticbooloperator!=(MyType lhs, MyType rhs)
{
return !(lhs == rhs);
}
Object.Equals()
in terms of a type-safe version of Equals()
to avoid unnecessary boxing and unboxing. publicoverrideintGetHashCode()
{
return m_data.GetHashCode();
}
publicoverrideboolEquals(object obj)
{
if(!(obj is MyType))
returnfalse;
returnthis.Equals((MyType) obj);
}
publicboolEquals(MyType rhs)
{
// Call this if m_data is a value type
return m_data.Equals(rhs.m_data);
// Call this if m_data is a reference type//
return Object.Equals(m_data, rhs.m_data);
}
publicstaticbooloperator==(MyType lhs, MyType rhs)
{
return lhs.Equals(rhs);
}
publicstaticbooloperator!=(MyType lhs, MyType rhs)
{
return !lhs.Equals(rhs);
}
[Flags]
attribute. <summary>
, <param>
and <returns>
elements. [assembly:CLSCompliant(true)]
attribute in the AssemblyInfo.cs file. If it is convenient to add a non-CLS compliant public member add a [CLSCompliant(false)]
attribute to it. private
members. If other classes in the same assembly need access, then declare them as internal
members. Try to expose as little as possible without sacrificing usability. string
s are immutable objects and always create a new copy for all the mutating operations, which makes it inefficient for assembling strings. StringBuilder
is a better choice for this task. object.MemberWiseClone()
provides shallow copying. Implement ICloneable
to provide deep copy for classes. ICloneable.Clone()
is usually implemented in terms of a copy constructor. publicMyType(MyType rhs)
{
m_data = rhs.m_data;
}
publicobjectClone()
{
returnnew MyType(this); //
}
publicobjectthis[int index]
{
get { return m_data[index]; }set { m_data[index] = value; }
}
foreach
loop it must implement IEnumerable
. IEnumerable.GetEnumerator()
returns a class the implements IEnumerator
. publicclass MyCollection: IEnumerable
{
public IEnumerator GetEnumerator()
{
returnnewMyCollectionEnumerator(this);
}
}
IEnumerator
for a "collection"class is usually implemented in a private class. An enumerator remains valid as long as the collection remains unchanged. If changes are made to the collection, such as adding, modifying or deleting elements, the enumerator is irrecoverably invalidated and the next call to MoveNext
or Reset
throws an InvalidOperationException
. If the collection is modified between MoveNext
and Current
, Current
will return the element that it is set to, even if the enumerator is already invalidated. privateclass MyCollectionEnumerator: IEnumerator
{
public MyCollectionEnumerator(MyCollection col)
{
m_col = col;
m_lastChanged = col.LastChanged;
}
publicboolMoveNext()
{
if(m_lastChanged != m_col.LastChanged)
thrownew InvalidOperationException();
if(++m_index >= m_col.Data.Count)
returnfalse;
returntrue;
}
publicvoidReset()
{
if(m_lastChanged != m_col.LastChanged)
thrownew InvalidOperationException();
m_index = -1;
}
publicobjectCurrent
{
get { return m_col.Data[m_index]; }
}
}
IDisposable
... publicclass MyClass
{
...
public ~MyClass()
{
Dispose(false);
}
publicvoid Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);// Finalization is now unnecessary
}
protectedvirtualvoid Dispose(bool disposing)
{
if(!m_disposed)
{
if(disposing)
{
// Dispose managed resources
}
// Dispose unmanaged resources
}
m_disposed = true;
}
privatebool m_disposed = false;
}
And use the using
statement to dispose resources as soon the object goes out of scope using(MyClass c = new MyClass())
{
...
} // The compiler will call Dispose() on c here
try
{
...
}
catch(Exception ex)
{
Console.WriteLine("Opps! Something failed: {0}", ex.Message);
}
publicvoid DivByZero()
{
int x = 1 / 0; // Our caller better be ready to deal with this!
}
try
{
...
}
catch(Exception ex)
{
// do some cleanupthrow;
}
try
{
...
}
catch(Exception ex)
{
thrownew Exception("Something really bad happened!", ex);
}
try
{
int i = 1 / 0;
}
catch(DivideByZeroException ex) // Instead of catch(Exception ex)
{
...
}
System.ApplicationException
, not from System.Exception
.