I would like to know the best way to compare 2 complex objects to know if they are equal by value, ie, their properties are the same? I tried the serialize method and not sure why they are not equal by value
Dim stream As New MemoryStream()
Dim bstream As New MemoryStream()
Dim clientOne As Jewellery.ClientInfo = New Jewellery.ClientInfo(New Jewellery.Company("a", "", "", "b", "", "e"), New Jewellery.Customer("a", "b", "c", "d", "", "", "", "f"))
Dim clientTwo As Jewellery.ClientInfo = New Jewellery.ClientInfo(New Jewellery.Company("a", "", "", "b", "", "e"), New Jewellery.Customer("a", "b", "c", "d", "", "", "", "f"))
formatter.Serialize(stream, clientOne)
formatter.Serialize(bstream, clientTwo)
Dim streamOneBytes As Byte() = stream.ToArray()
Dim streamTwoBytes As Byte() = bstream.ToArray()
Dim streamToCompareBytes As Byte() = streamToCompare.ToArray()
Dim i As Int16 = 0
Dim flag As Boolean
If streamOneBytes.Length <> streamTwoBytes.Length Then
MsgBox("False")
flag = False
Else
While i < streamOneBytes.Count
If streamOneBytes(i) <> streamTwoBytes(i) Then
flag = "False"
Else : flag = "True"
End If
i = i + 1
End While
End If
As you see in the above code, when I initialize 2 objects of the same type and compare, it works correctly. But when I add one object to say a List and then retrieve and compare to an object of similar type, it fails saying the binary width are different for both. Any advice? Thanks
public class IntegerPropertyEqualityCompare : IEqualityComparer<Main>
{
#region IEqualityComparer<Main> Members
public bool Equals( Main x, Main y )
{
return x.IntegerProperty == y.IntegerProperty;
}
public int GetHashCode( Main obj )
{
return obj.IntegerProperty.GetHashCode() ^ obj.StringProperty.GetHashCode();
}
#endregion
}
public class StringPropertyEqualityCompare : IEqualityComparer<Main>
{
#region IEqualityComparer<Main> Members
public bool Equals( Main x, Main y )
{
return x.StringProperty == y.StringProperty;
}
public int GetHashCode( Main obj )
{
return obj.IntegerProperty.GetHashCode() ^ obj.StringProperty.GetHashCode();
}
#endregion
}
public class AllPropertiesEqualityCompare : IEqualityComparer<Main>
{
#region IEqualityComparer<Main> Members
public bool Equals( Main x, Main y )
{
return ( x.IntegerProperty == y.IntegerProperty ) && ( x.StringProperty == y.StringProperty );
}
public int GetHashCode( Main obj )
{
return obj.IntegerProperty.GetHashCode() ^ obj.StringProperty.GetHashCode();
}
#endregion
}
public abstract class Main
{
public int IntegerProperty { get; set; }
public string StringProperty { get; set; }
public bool Equals( IEqualityComparer<Main> comparer, Main other )
{
return comparer.Equals( this, other );
}
}
public class ConcreteA : Main
{ }
public class ConcreteB : Main
{ }
class TemporaryTest
{
public static void Run()
{
var a = new ConcreteA();
a.IntegerProperty = 1;
a.StringProperty = "some";
var b = new ConcreteB();
b.IntegerProperty = 1;
a.StringProperty = "other";
Console.WriteLine( a.Equals( new IntegerPropertyEqualityCompare(), b ) );
Console.WriteLine( a.Equals( new StringPropertyEqualityCompare(), b ) );
Console.WriteLine( a.Equals( new AllPropertiesEqualityCompare(), b ) );
}
}
Maybe then somthing like this?
I'm not sure that I understand what you want to do.
So I go with IComparable<T>.
Related
My method toString() is supposed to return a string representation of the stack. The string representation consists of the stacks's elements in the order they are stored, enclosed in square brackets. My problem is that I am now returning [element0, element1, element2, element3, element4] so I wonder if there is there a simple way to return the string in reverse order i.e. to return [element4, element3, element2, element1, element0] instead?
public class Stack<E> implements IStack<E> {
public String toString() {
String str = "[";
if (head != null) {
str += head.getmElement();
Node<E> tempNode = head.getmNextNode();
while (tempNode != null) {
str += ", " + tempNode.getmElement();
tempNode = tempNode.getmNextNode();
}
}
str += "]";
return str; }
Node class:
public class Node<E> {
private E mElement;
private Node<E> mNextNode;
Node(E data) {
this.setmElement(data);
}
public E getmElement() {
return this.mElement;
}
public void setmElement(E element) {
this.mElement = element;
}
public Node<E> getmNextNode()
{
return this.mNextNode;
}
public void setmNextNode(Node<E> node)
{
this.mNextNode = node;
}}
You could use a StringBuilder and insert every element at the beginning instead of appending it:
public String toString() {
StringBuilder sb = new StringBuilder("[");
if (head != null) {
sb.append(head.getmElement());
Node<E> tempNode = head.getmNextNode();
while (tempNode != null) {
sb.insert(1, ", ").inser(1, tempNode.getmElement());
tempNode = tempNode.getmNextNode();
}
}
sb.append("]");
return sb.toString();
}
Your list is only forward linked, so you could use a temporary ArrayList and add each element at the index 0.
I am trying to serialize Guava Range objects to JSON using Gson, however the default serialization fails, and I'm unsure how to correctly implement a TypeAdapter for this generic type.
Gson gson = new Gson();
Range<Integer> range = Range.closed(10, 20);
String json = gson.toJson(range);
System.out.println(json);
Range<Integer> range2 = gson.fromJson(json,
new TypeToken<Range<Integer>>(){}.getType());
System.out.println(range2);
assertEquals(range2, range);
This fails like so:
{"lowerBound":{"endpoint":10},"upperBound":{"endpoint":20}}
PASSED: typeTokenInterface
FAILED: range
java.lang.RuntimeException: Unable to invoke no-args constructor for
com.google.common.collect.Cut<java.lang.Integer>. Register an
InstanceCreator with Gson for this type may fix this problem.
at com.google.gson.internal.ConstructorConstructor$12.construct(
ConstructorConstructor.java:210)
...
Note that the default serialization actually loses information - it fails to report whether the endpoints are open or closed. I would prefer to see it serialized similar to its toString(), e.g. [10‥20] however simply calling toString() won't work with generic Range instances, as the elements of the range may not be primitives (Joda-Time LocalDate instances, for example). For the same reason, implementing a custom TypeAdapter seems difficult, as we don't know how to deserialize the endpoints.
I've implemented most of a TypeAdaptorFactory based on the template provided for Multimap which ought to work, but now I'm stuck on the generics. Here's what I have so far:
public class RangeTypeAdapterFactory implements TypeAdapterFactory {
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
Type type = typeToken.getType();
if (typeToken.getRawType() != Range.class
|| !(type instanceof ParameterizedType)) {
return null;
}
Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0];
TypeAdapter<?> elementAdapter = (TypeAdapter<?>)gson.getAdapter(TypeToken.get(elementType));
// Bound mismatch: The generic method newRangeAdapter(TypeAdapter<E>) of type
// GsonUtils.RangeTypeAdapterFactory is not applicable for the arguments
// (TypeAdapter<capture#4-of ?>). The inferred type capture#4-of ? is not a valid
// substitute for the bounded parameter <E extends Comparable<?>>
return (TypeAdapter<T>) newRangeAdapter(elementAdapter);
}
private <E extends Comparable<?>> TypeAdapter<Range<E>> newRangeAdapter(final TypeAdapter<E> elementAdapter) {
return new TypeAdapter<Range<E>>() {
#Override
public void write(JsonWriter out, Range<E> value) throws IOException {
if (value == null) {
out.nullValue();
return;
}
String repr = (value.lowerBoundType() == BoundType.CLOSED ? "[" : "(") +
(value.hasLowerBound() ? elementAdapter.toJson(value.lowerEndpoint()) : "-\u221e") +
'\u2025' +
(value.hasLowerBound() ? elementAdapter.toJson(value.upperEndpoint()) : "+\u221e") +
(value.upperBoundType() == BoundType.CLOSED ? "]" : ")");
out.value(repr);
}
public Range<E> read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
String[] endpoints = in.nextString().split("\u2025");
E lower = elementAdapter.fromJson(endpoints[0].substring(1));
E upper = elementAdapter.fromJson(endpoints[1].substring(0,endpoints[1].length()-1));
return Range.range(lower, endpoints[0].charAt(0) == '[' ? BoundType.CLOSED : BoundType.OPEN,
upper, endpoints[1].charAt(endpoints[1].length()-1) == '[' ? BoundType.CLOSED : BoundType.OPEN);
}
};
}
}
However the return (TypeAdapter<T>) newRangeAdapter(elementAdapter); line has a compilation error and I'm now at a loss.
What's the best way to resolve this error? Is there a better way to serialize Range objects that I'm missing? What about if I want to serialize RangeSets?
Rather frustrating that the Google utility library and Google serialization library seem to require so much glue to work together :(
This feels somewhat like reinventing the wheel, but it was a lot quicker to put together and test than the time spent trying to get Gson to behave, so at least presently I'll be using the following Converters to serialize Range and RangeSet*, rather than Gson.
/**
* Converter between Range instances and Strings, essentially a custom serializer.
* Ideally we'd let Gson or Guava do this for us, but presently this is cleaner.
*/
public static <T extends Comparable<? super T>> Converter<Range<T>, String> rangeConverter(final Converter<T, String> elementConverter) {
final String NEG_INFINITY = "-\u221e";
final String POS_INFINITY = "+\u221e";
final String DOTDOT = "\u2025";
return new Converter<Range<T>, String>() {
#Override
protected String doForward(Range<T> range) {
return (range.hasLowerBound() && range.lowerBoundType() == BoundType.CLOSED ? "[" : "(") +
(range.hasLowerBound() ? elementConverter.convert(range.lowerEndpoint()) : NEG_INFINITY) +
DOTDOT +
(range.hasUpperBound() ? elementConverter.convert(range.upperEndpoint()) : POS_INFINITY) +
(range.hasUpperBound() && range.upperBoundType() == BoundType.CLOSED ? "]" : ")");
}
#Override
protected Range<T> doBackward(String range) {
String[] endpoints = range.split(DOTDOT);
Range<T> ret = Range.all();
if(!endpoints[0].substring(1).equals(NEG_INFINITY)) {
T lower = elementConverter.reverse().convert(endpoints[0].substring(1));
ret = ret.intersection(Range.downTo(lower, endpoints[0].charAt(0) == '[' ? BoundType.CLOSED : BoundType.OPEN));
}
if(!endpoints[1].substring(0,endpoints[1].length()-1).equals(POS_INFINITY)) {
T upper = elementConverter.reverse().convert(endpoints[1].substring(0,endpoints[1].length()-1));
ret = ret.intersection(Range.upTo(upper, endpoints[1].charAt(endpoints[1].length()-1) == ']' ? BoundType.CLOSED : BoundType.OPEN));
}
return ret;
}
};
}
/**
* Converter between RangeSet instances and Strings, essentially a custom serializer.
* Ideally we'd let Gson or Guava do this for us, but presently this is cleaner.
*/
public static <T extends Comparable<? super T>> Converter<RangeSet<T>, String> rangeSetConverter(final Converter<T, String> elementConverter) {
return new Converter<RangeSet<T>, String>() {
private final Converter<Range<T>, String> rangeConverter = rangeConverter(elementConverter);
#Override
protected String doForward(RangeSet<T> rs) {
ArrayList<String> ls = new ArrayList<>();
for(Range<T> range : rs.asRanges()) {
ls.add(rangeConverter.convert(range));
}
return Joiner.on(", ").join(ls);
}
#Override
protected RangeSet<T> doBackward(String rs) {
Iterable<String> parts = Splitter.on(",").trimResults().split(rs);
ImmutableRangeSet.Builder<T> build = ImmutableRangeSet.builder();
for(String range : parts) {
build.add(rangeConverter.reverse().convert(range));
}
return build.build();
}
};
}
*For inter-process communication, Java serialization would likely work just fine, as both classes implement Serializable. However I'm serializing to disk for more permanent storage, meaning I need a format I can trust won't change over time. Guava's serialization doesn't provide that guarantee.
Here is a Gson JsonSerializer and JsonDeserializer that generically supports a Range: https://github.com/jamespedwards42/Fava/wiki/Range-Marshaller
#Override
public JsonElement serialize(final Range src, final Type typeOfSrc, final JsonSerializationContext context) {
final JsonObject jsonObject = new JsonObject();
if ( src.hasLowerBound() ) {
jsonObject.add( "lowerBoundType", context.serialize( src.lowerBoundType() ) );
jsonObject.add( "lowerBound", context.serialize( src.lowerEndpoint() ) );
} else
jsonObject.add( "lowerBoundType", context.serialize( BoundType.OPEN ) );
if ( src.hasUpperBound() ) {
jsonObject.add( "upperBoundType", context.serialize( src.upperBoundType() ) );
jsonObject.add( "upperBound", context.serialize( src.upperEndpoint() ) );
} else
jsonObject.add( "upperBoundType", context.serialize( BoundType.OPEN ) );
return jsonObject;
}
#Override
public Range<? extends Comparable<?>> deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context) throws JsonParseException {
if ( !( typeOfT instanceof ParameterizedType ) )
throw new IllegalStateException( "typeOfT must be a parameterized Range." );
final JsonObject jsonObject = json.getAsJsonObject();
final JsonElement lowerBoundTypeJsonElement = jsonObject.get( "lowerBoundType" );
final JsonElement upperBoundTypeJsonElement = jsonObject.get( "upperBoundType" );
if ( lowerBoundTypeJsonElement == null || upperBoundTypeJsonElement == null )
throw new IllegalStateException( "Range " + json
+ "was not serialized with this serializer! The default serialization does not store the boundary types, therfore we can not deserialize." );
final Type type = ( ( ParameterizedType ) typeOfT ).getActualTypeArguments()[0];
final BoundType lowerBoundType = context.deserialize( lowerBoundTypeJsonElement, BoundType.class );
final JsonElement lowerBoundJsonElement = jsonObject.get( "lowerBound" );
final Comparable<?> lowerBound = lowerBoundJsonElement == null ? null : context.deserialize( lowerBoundJsonElement, type );
final BoundType upperBoundType = context.deserialize( upperBoundTypeJsonElement, BoundType.class );
final JsonElement upperBoundJsonElement = jsonObject.get( "upperBound" );
final Comparable<?> upperBound = upperBoundJsonElement == null ? null : context.deserialize( upperBoundJsonElement, type );
if ( lowerBound == null && upperBound != null )
return Range.upTo( upperBound, upperBoundType );
else if ( lowerBound != null && upperBound == null )
return Range.downTo( lowerBound, lowerBoundType );
else if ( lowerBound == null && upperBound == null )
return Range.all();
return Range.range( lowerBound, lowerBoundType, upperBound, upperBoundType );
}
Here is a straight forward solution. Works very well
import com.google.common.collect.BoundType;
import com.google.common.collect.Range;
import com.google.gson.*;
import java.lang.reflect.Type;
public class GoogleRangeAdapter implements JsonSerializer, JsonDeserializer {
public static String TK_hasLowerBound = "hasLowerBound";
public static String TK_hasUpperBound = "hasUpperBound";
public static String TK_lowerBoundType = "lowerBoundType";
public static String TK_upperBoundType = "upperBoundType";
public static String TK_lowerBound = "lowerBound";
public static String TK_upperBound = "upperBound";
#Override
public Object deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonObject jsonObject = (JsonObject)json;
boolean hasLowerBound = jsonObject.get(TK_hasLowerBound).getAsBoolean();
boolean hasUpperBound = jsonObject.get(TK_hasUpperBound).getAsBoolean();
if (!hasLowerBound && !hasUpperBound) {
return Range.all();
}
else if (!hasLowerBound && hasUpperBound){
double upperBound = jsonObject.get(TK_upperBound).getAsDouble();
BoundType upperBoundType = BoundType.valueOf(jsonObject.get(TK_upperBoundType).getAsString());
if (upperBoundType == BoundType.OPEN)
return Range.lessThan(upperBound);
else
return Range.atMost(upperBound);
}
else if (hasLowerBound && !hasUpperBound){
double lowerBound = jsonObject.get(TK_lowerBound).getAsDouble();
BoundType lowerBoundType = BoundType.valueOf(jsonObject.get(TK_lowerBoundType).getAsString());
if (lowerBoundType == BoundType.OPEN)
return Range.greaterThan(lowerBound);
else
return Range.atLeast(lowerBound);
}
else {
double lowerBound = jsonObject.get(TK_lowerBound).getAsDouble();
double upperBound = jsonObject.get(TK_upperBound).getAsDouble();
BoundType upperBoundType = BoundType.valueOf(jsonObject.get(TK_upperBoundType).getAsString());
BoundType lowerBoundType = BoundType.valueOf(jsonObject.get(TK_lowerBoundType).getAsString());
if (lowerBoundType == BoundType.OPEN && upperBoundType == BoundType.OPEN)
return Range.open(lowerBound, upperBound);
else if (lowerBoundType == BoundType.OPEN && upperBoundType == BoundType.CLOSED)
return Range.openClosed(lowerBound, upperBound);
else if (lowerBoundType == BoundType.CLOSED && upperBoundType == BoundType.OPEN)
return Range.closedOpen(lowerBound, upperBound);
else
return Range.closed(lowerBound, upperBound);
}
}
#Override
public JsonElement serialize(Object src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject jsonObject = new JsonObject();
Range<Double> range = (Range<Double>)src;
boolean hasLowerBound = range.hasLowerBound();
boolean hasUpperBound = range.hasUpperBound();
jsonObject.addProperty(TK_hasLowerBound, hasLowerBound);
jsonObject.addProperty(TK_hasUpperBound, hasUpperBound);
if (hasLowerBound) {
jsonObject.addProperty(TK_lowerBound, range.lowerEndpoint());
jsonObject.addProperty(TK_lowerBoundType, range.lowerBoundType().name());
}
if (hasUpperBound) {
jsonObject.addProperty(TK_upperBound, range.upperEndpoint());
jsonObject.addProperty(TK_upperBoundType, range.upperBoundType().name());
}
return jsonObject;
}
}
I cannot seem to find anywhere, any examples on how to make use of the GroupFormatter delegate to allow me to add footers to my groups when using the ObjectListView control.
Does anyone have any examples that could demonstrate this? I want to remove the text from the group header and add a footer (different text per footer). As well as changing font, etc.
Any examples would be very helpful.
You can analyze the code for the
public void MakeGroupies<T>(T[] values, string[] descriptions, object[] images, string[] subtitles, string[] tasks)
method of the ObjectListView class. That explicitly sets the GroupKeyGetter, GroupKeyToTitleConverter and GroupFormatter property delegates.
This is C# but your VB adaptation should be straightforward. I am using this small test class as the object type to bind to the list view.
public class TestClass
{
private readonly string _s;
private readonly float _f;
public TestClass( string p1, float p2 )
{
this._s = p1;
this._f = p2;
}
[OLVColumn(DisplayIndex = 1, Name="S", Title="String")]
public string S {get {return this._s;}}
[OLVColumn( DisplayIndex = 2, Name = "F", Title = "Float" )]
public float F {get {return this._f;}}
}
So as not to manually define column traits I am using attributes inside the bound object and a
BrightIdeasSoftware.Generator.GenerateColumns( this.olv, typeof( TestClass ) );
call in the form/user control where I am using the list view. In fact here is the method that completely isolates ObjectListView configuration:
void SetData( TestClass[] objects )
{
// build list columns
Generator.GenerateColumns( this.olv, typeof( TestClass ) );
// use groups and make current column the priimary sort column
this.olv.ShowGroups = true;
this.olv.SortGroupItemsByPrimaryColumn = false;
// loop through columns and set properties
foreach( OLVColumn col in this.olv.Columns )
{
col.Groupable = true;
col.Sortable = true;
if( col.Name == "F" )
{
col.MakeGroupies<float>( new float[] { 10f, 100f, 1000f }, new string[] { "<10", "10-100", "100-1000", ">1000" } );
}
else if( col.Name == "S" )
{
col.UseInitialLetterForGroup = false;
//
col.GroupKeyGetter = ( obj ) =>
{
TestClass tc = (TestClass)obj;
switch( char.ToLower( tc.S[0] ) )
{
case 'a':
case 'e':
case 'i':
case 'o':
case 'u': return true;
default: return false;
}
};
//
col.GroupKeyToTitleConverter = ( o ) => { bool b = (bool)o; return b ? "vowel" : "consonant"; };
//
col.GroupFormatter = ( /*OLVGroup*/ group, /*GroupingParameters*/ parms ) =>
{
string s = string.Format ("{0} {1}", group.GroupId, group.Id);
//group.BottomDescription = "BottomDescription: " + s;
//group.TopDescription = "TopDescription: " + s;
group.Footer = "Footer: " + s;
};
}
}
//
this.olv.RebuildColumns();
//
this.olv.SetObjects( objects );
}
You will definitely have one different footer per each group.
I've read a possible solution to this, but would require a lot of rewriting, the possible solution is linked here, but there wouldn't be any sense to doing it that way if I am just a couple words off in my dropdownlistfor.
I'm having an issue with my dropdownlistfor as this is all new to me:
#Html.DropDownListFor(model => model.pageID, new SelectList (Enum.GetNames(typeof(PageIndex)), EnumHelper.GetSelectedItemList<PageIndex>().SelectedValue))
Trying to grab the "description" of my enum values as the drop down lists text values, then have an integer value returned to the database on POST.
Here's my enum:
public enum PageIndex : int
{
[Description("Developmental Disabilities Tip Sheet")]
ddTipSheets = 1,
[Description("Hiiiiiiiiiiiiiiiiiiii")]
Example1 = 2,
[Description("I don't know what I'm doing")]
Example2 = 3
};
and my EnumHelper:
public class EnumHelper
{
public static SelectList GetSelectedItemList<T>() where T : struct
{
T t = default(T);
if (!t.GetType().IsEnum) { throw new ArgumentNullException("Please make sure that T is of Enum Type"); }
var nameList = t.GetType().GetEnumNames();
int counter = 0;
Dictionary<int, String> myDictionary = new Dictionary<int, string>();
if (nameList != null && nameList.Length > 0)
{
foreach (var name in nameList)
{
T newEnum = (T) Enum.Parse(t.GetType(), name);
string description = getDescriptionFromEnumValue(newEnum as Enum);
if (!myDictionary.ContainsKey(counter))
{
myDictionary.Add(counter, description);
}
counter++;
}
counter = 0;
return new SelectList(myDictionary, "Key", "Value");
}
return null;
}
private static string getDescriptionFromEnumValue(Enum value)
{
DescriptionAttribute descriptionAttribute =
value.GetType()
.GetField(value.ToString())
.GetCustomAttributes(typeof(DescriptionAttribute), false)
.SingleOrDefault() as DescriptionAttribute;
return descriptionAttribute == null ?
value.ToString() : descriptionAttribute.Description;
}
}
What is the best way for mapping collection of objects in NHibernate?
Now I am using bags, but maybe another approach can be more efficient?
Before wore Bag and ClassMapping, something very similar to this:
Bag(am => am.AlumnoMateriaId, map =>
{
map.Table("Calificacion");
map.Cascade(Cascade.None);
map.Key(k =>
{
k.Column("AlumnoId");
k.ForeignKey("FK_Calificacion_AlumnoMateria");
});
}, rel => rel.ManyToMany(p => p.Column("AlumnoId")));
Now Use the NHibernate.Mapping.Attributes, and a class would look like:
using NHMA = NHibernate.Mapping.Attributes;
[NHMA.Class(Table = " Calificacion ")]
public class Calificacion
{
[NHMA.Id(0, Name = "id", TypeType = typeof(Int32), Column = "Id", Access = "field")]
[NHMA.Generator(1, Class = "native")]
public Int32 id;
public virtual Int32 Id
{
get { return id; }
set { id = value; }
}
[NHMA.ManyToOne(2, Name = "calificacion", Access = "field", Column = "CalificacionID",
Class = "Calificacion", ClassType = typeof(Calificacion),
ForeignKey = "FK_Calificacion_Alumno", NotNull = false)]
private Calificacion calificaion;
public virtual Calificacion Calificaion
{
get { return calificaion; }
set { calificaion = value; }
}
[NHMA.Property(Name = "nombre", Access = "field", Column = "Nombre", Length = 50)]
private string nombre;
public virtual string Nombre
{
get { return nombre; }
set { nombre = value; }
}
}