0

As you can see under I have create getter and setter for class property. I'm wondering, is it fine to set it up like I did under? I didn't use the private variable in the get, is it fine or should I use it? Could someone correct me how my class in this case, should look like, if my approach is wrong? The class is CloneAble

public int? Id { get; set; }
public string Code { get; set; }

private string _name;
public string Name
{
    get
    {
        if (this.Id == null)
            return null;
        else
            return this.Id+ "/" + this.Code;
    }
    set
    {
        if (this._name != value)
        {
            this._name= value;
        }
    }
}

public bool IsValid
{
    get
    {
        if (this.Id == null)
            return false;
        else
            return true;
    }
}

The class is Cloneable:

public object Clone()
{
    var elem= new Element();

    this.SetPropertyValues(elem);

    return elem;
}

private void SetPropertyValues(elem)
{
    var propertyInfo = this.GetType().GetProperties().Where(p => p.CanWrite && (p.PropertyType.IsValueType || p.PropertyType.IsEnum || p.PropertyType.Equals(typeof(System.String))));

    foreach (PropertyInfo property in propertyInfo)
    {
        if (property.CanWrite)
        {
            property.SetValue(elem, property.GetValue(this, null), null);
        }
    }
}
  • 1
    What is Name supposed to mean ? you set private field _name it with any input you give to setter Name, *but*, when you get it it doesn't give back _name, but a combination of Id and Code. While this is syntaxicly perfectly valid, I'm not sure what's your goal here. How do you use `_name` in the rest of the class ? – Pac0 Sep 09 '19 at 11:32
  • 1
    also, last point is simply `return this.Id != null` – Pac0 Sep 09 '19 at 11:33
  • 1
    When you use `get; set;` you cannot see the private field in your code but internally a private field is used. So it really depends on what you prefer. But the code inside your getters and setters is unnecessary. For example `IsValid` whill always return true as an Integer cannot be null. – Erik T. Sep 09 '19 at 11:35
  • as it stands the setter does nothing so shouldn't be there (the _name field is never referenced).Do you need a setter? You could just use a get only property. – Tim Rutter Sep 09 '19 at 11:38
  • 1
    Getters and setters exist specifically to allow custom logic when getting or setting properties, so the fact that you have custom logic is fine per se. It is also not an error to return a calculated value from a getter - you decide whether it is okay in your case or it would be better to cache the value. What is not fine is that the getter returns not what the setter sets, which is highly confusing. – GSerg Sep 09 '19 at 11:39
  • 1
    (this.Id==null) results in a compiler warning _"Warning CS0472 The result of the expression is always 'false' since a value of type 'int' is never equal to 'null' of type 'int?'") – PaulF Sep 09 '19 at 11:41
  • Shouldn't this be on https://codereview.stackexchange.com/ ? – trollingchar Sep 09 '19 at 11:43
  • What are you trying to achieve here - you can set Name to one thing, but when you get the Name back it is something different? – PaulF Sep 09 '19 at 11:45
  • What you could do is put an null check in the Setter for value. If ``` this._name != value``` then ```this._name = value```, but what if ```value``` is ```null```? It depends on your business logic what you want to achieve and then you will know what is appropriate. – Edwardcho Vaklinov Sep 09 '19 at 11:50
  • The reason I have a setter in name is because the class is cloneable and I get an error which say it cannot set value on Name, since it has no setter, if i remove it. Lets say if I change the Code part, then it should also change the name. –  Sep 09 '19 at 12:12
  • @FredrikLinger You can circumvent the cloning issue by using a [Copy-Contructor](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/how-to-write-a-copy-constructor) instead (for example). And here is an interesting alternative: https://stackoverflow.com/a/3345529/982149 – Fildor Sep 09 '19 at 12:19
  • 1
    @Fildor I have udated the description with my clone method. –  Sep 09 '19 at 12:26
  • There you go. Much too complicated. Just make a new instance and copy the values. You can also access private fields there. – Fildor Sep 09 '19 at 12:32

3 Answers3

1

I think you can get rid of your struggles by simplifying the Clone Method:

// Private parameterized CTOR
private Element( int id, string code ) 
{ 
    this.Id = id;
    this.Code = code;
}

// Simplyfied Clone
public object Clone()
{
    return new Element(Id, Code);
}

Now you can have the readonly Property Name and Clone:

public string Name => $"{Id}/{Code}";
Fildor
  • 14,510
  • 4
  • 35
  • 67
0

This code works, but what is the utility of _name? You can set its value but not the opposite. In addition, creating IsValid is not very useful, as Id is a public attribute; you could simply create a method whose value is this.Id != wrongValue and make Id private.

The result would be the following:

private int Id { get; set; }
public string Code { get; set; }

private readonly int IMPOSSIBLE_ID = -1000;

private string _name;
public string Name
{
    get {
        return (this.Id == null) ? null : this.Id+ "/" + this.Code;
    }
    set {
        if (this._name != value)
            this._name= value;
    }
}

public bool IsValid() => return this.Id != IMPOSSIBLE_ID;

UPDATE : now Id is a nullable integer

private int? Id { get; set; }
public string Code { get; set; }

private string _name;
public string Name
{
    get {
        return (this.Id == null) ? null : this.Id+ "/" + this.Code;
    }
    set {
        if (this._name != value)
            this._name= value;
    }
}

public bool IsValid() => return this.Id != null;
0009laH
  • 1,960
  • 13
  • 27
  • 2
    an int cannot be null – Tim Rutter Sep 09 '19 at 11:40
  • (this.Id==null) results in a compiler warning _"Warning CS0472 The result of the expression is always 'false' since a value of type 'int' is never equal to 'null' of type 'int?'") – PaulF Sep 09 '19 at 11:41
  • this still doesn't explain the use of the setter for name. – Tim Rutter Sep 09 '19 at 11:46
  • Let's say if Id is a nullable int. How would you correct my code to make it correct? By the way I have set it up, is it fine to do it like this? –  Sep 09 '19 at 11:54
  • If it is nullable then `IMPOSSIBLE_ID` is useless; you can delete l.4 and change last line to `public bool IsValid() => return this.Id != null;`. As for Name attribute... I don't know exactly how would you like to use it so I don't know how to correct it. – 0009laH Sep 09 '19 at 11:59
  • @Fildor The default value for a nullable is `null`, so `IsValid()` returns `false` unless we define a value for `Id`. – 0009laH Sep 09 '19 at 12:07
  • 1
    @0009laH There is no nullable in your example code. Ah wait. In your comment. OK, Nevermind. – Fildor Sep 09 '19 at 12:14
  • Could I set the private variable as combination of id and code in the getter and return the private variable there? Would it be more correct? I think the parsing is a bit overkill for my case, as I kind of need the setter only because of the Cloneable type... –  Sep 09 '19 at 12:16
  • How did you implement cloning @FredrikLinger? – Fildor Sep 09 '19 at 12:17
0

First of all int Id can never be null since int is struct; that's why this.Id == null is always true. You can either make Id be nullable:

 public int? Id { get; set; }

or change this.Id == null into, say, this.Id == 0

 // Let Id be nullable 
 public int? Id { get; set; }
 public string Code { get; set; }

Since Name can be easily computed, you don't want _name. Another issue with Name setter: you can assign, say, "bla-bla-bla" to Name then read it again and get something unexpected:

 var demo = MyClass();

 // "0/"
 Console.WriteLine(demo.Name);

 demo.Name = "123/Test";

 // "0/" instead of expected "123/Test";
 Console.WriteLine(demo.Name);   

That's why I suggest either to remove the setter:

 public string Name {
   get {
     return Id.HasValue
       ? $"{Id}/{Code}"
       : ""; // often, it's better return an empty string, not null
   }
 }   

Or implement some kind of parsing:

 public string Name {
   get {
     return Id.HasValue
       ? $"{Id}/{Code}"
       : ""; // often, it's better return an empty string, not null
   }
   set {
     if (string.IsNullOrEmpty(value)) {
       Id = null;
       Code = ""; // often, it's better return an empty string, not null
     } 
     else {
       string[] parts = string.Split(new char[] {'/'}, 2);

       if (string.IsNullOrEmpty(parts[0])) {
         Id = null;
         Code = parts.Length > 1 ? parts[1] : ""; 
       }
       else if (int.TryParse(parts[0], out int id)) {
         Id = id;
         Code = parts.Length > 1 ? parts[1] : ""; 
       } 
       else 
         throw new FormatException("Name is inincorrect format.");
     }
   }
 }   

Finally, IsValid can be shortened into

 public bool IsValid => Id.HasValue;
Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215
  • The reason I have a setter in name is because the class is cloneable and I get an error which say it cannot set value on Name, since it has no setter, if i remove it. Lets say if I change the Code part, then it should also change the name. –  Sep 09 '19 at 11:59
  • If the class implements `ICloneable` you should either check interface implementation (remove `.Name = this.Name;` fragment) or implement parsing within `set` – Dmitry Bychenko Sep 09 '19 at 12:06
  • Could you give me an example how it should look like? :) –  Sep 09 '19 at 12:09
  • @Fredrik Linger: Sorry, but I've provided `Name` `set` which *parses* value provided and assignes `Id` as well as `Code`. Please, see my second (much longer) version of `Name` property – Dmitry Bychenko Sep 09 '19 at 12:12