Click here to Skip to main content
15,890,438 members
Articles / Programming Languages / C#

Using Keyword 'new' in Class Member Definitions

Rate me:
Please Sign up or sign in to vote.
4.37/5 (14 votes)
22 Aug 2017CPOL6 min read 12.1K   7   15
Do you know, that keyword 'new' can be used in a definition of class members? I knew about it, but have never used it for many years I work in the software industry. But recently, I have found a couple of scenarios where this opportunity is very useful. Here, I want to share my findings.

In class member definition keyword 'new' works similar to overloading of methods. But it supports scenarios, which are not allowed for overloading. For example, you can make read-only property writable. You are not allowed to do it using overload or override:

C#
public abstract class Parent
{
    public virtual int Value
    {
        get { return 0; }
    }
}

public class Child : Parent
{
    public override int Value
    {
        get;
        set; // Compilation error here
    }
}

But you can do it using 'new' keyword:

C#
public abstract class Parent
{
    public int Value
    {
        get { return 0; }
    }
}

public class Child : Parent
{
    public new int Value
    {
        get;
        set;
    }
}

Also, you can't change only the type of method result using overload or override:

C#
public abstract class Parent
{
    public virtual int GetValue()
    {
        return 0;
    }
}

public class Child : Parent
{
    public override string GetValue() // Compilation error
    {
        return string.Empty;
    }
}

But you also can do it with 'new' keyword:

C#
public abstract class Parent
{
    public int GetValue()
    {
        return 0;
    }
}

public class Child : Parent
{
    public new string GetValue()
    {
        return string.Empty;
    }
}

Nevertheless, there are some things you should know to use this possibility correctly. If you take a look at Avivo recommendations for writing code, you can find the following one:

Don't hide inherited members with new keyword

Even in this case, I want to show you a couple of examples, demonstrating, how your program can benefit from the 'new' keyword.

Making read-only Property Writable

Let's consider the following example. In your program, you model some items (tasks, posts, etc.). Each item has its priority (here, we'll model it as integer number). There are two types of items: system and custom. System items always must have priority of zero. For custom items, a user must have the ability to change the priority. How will we model classes of items? One approach is the following:

C#
public abstract class Item
{
    public virtual int Priority { get; set; }
}

public class CustomItem : Item { }

public class SystemItem : Item
{
    public override int Priority
    {
        get { return 0; }
        set { throw new InvalidOperationException(); }
    }
}

What is a problem with this design? The problem is that you still can write the following code without compilation errors:

C#
SystemItem item = new SystemItem();
item.Priority = 10;

Certainly, you'll get your error at runtime, but you can do better with the 'new' keyword. Take a look at the following code:

C#
public abstract class Item
{
    public int Priority
    {
        get { return 0; }
    }
}

public class CustomItem : Item
{
    public new int Priority { get; set; }
}

public class SystemItem : Item { }

Now the previous code will throw a compilation error. You are not allowed to set priority for system items.

So, are we done? Not yet. What do you think the following code will print:

C#
CustomItem customItem = new CustomItem();
customItem.Priority = 10;

Item item = customItem;
Console.WriteLine(item.Priority);

If you think it is 0, you are right. 'New' keyword does not work like overriding. After casting to the base class, code of old property will be called.

Can we somehow overcome this limitation? Yes, we can:

C#
public abstract class Item
{
    protected int _priority = 0;

    public int Priority
    {
        get { return _priority; }
    }
}

public class CustomItem : Item
{
    public new int Priority
    {
        get { return _priority; }
        set { _priority = value; }
    }
}

public class SystemItem : Item { }

As you can see, in both base and derived classes, we use a common protected field for work of Priority property. It allows us to get the correct value of this property even after casting of a CustomItem object to the base Item class.

There is one more thing you should know here. We have made read-only property writable. Can we do writable property read-only? At first glance, it looks like we can:

C#
public abstract class Item
{
    protected int _priority = 0;

    public int Priority
    {
        get { return _priority; }
        set { _priority = value; }
    }
}

public class CustomItem : Item { }

public class SystemItem : Item
{
    public new int Priority
    {
        get { return _priority; }
    }
}

The main problem with this design, from my point of view, is that we still can change the priority of SystemItem objects:

C#
SystemItem systemItem = new SystemItem();
Item item = systemItem;
item.Priority = 10;

Console.WriteLine(systemItem.Priority);

This code will print 10, although system items must always have priority of 0.

This is the reason why I think you should not try to make writable properties read-only.

Changing the Result Type of a Method

Let's consider another example. In our program, we want to have several different storages of items. Classes of these storages must have method GetItems, which returns a collection of Item objects.

C#
public abstract class Storage
{
    public IEnumerable<Item> GetItems()
    {
        return GetItemsFromDatabase();
    }

    private IEnumerable<Item> GetItemsFromDatabase()
    {
        ...
    }
}

Among all storages, we also have CustomStorage, that can contain only instances of CustomItem objects. For this storage, we would like to have GetItems method returning a collection of CustomItem objects. We can do it using 'new' keyword:

C#
public abstract class Storage
{
    public IEnumerable<Item> GetItems()
    {
        return GetItemsFromDatabase();
    }

    private IEnumerable<Item> GetItemsFromDatabase()
    {
        ...
    }
}

public class CommonStorage : Storage
{}

public class CustomStorage : Storage
{
    public new IEnumerable<CustomItem> GetItems()
    {
        return GetCustomItemsFromDatabase();
    }

    private IEnumerable<CustomItem> GetCustomItemsFromDatabase()
    {
        ...
    }
}

The problem with this code is the same as in the previous case. After casting object of CustomStorage type to Storage type, we'll get the implementation of GetItems from Storage class, which will lead to call of GetItemsFromDatabase method instead of GetCustomItemsFromDatabase. We can fix this problem using the same technique, as in the previous case, but here we'll use a protected field of delegate type:

C#
public abstract class Storage
{
    protected Func<IEnumerable<Item>> _getItems;

    protected Storage()
    {
        _getItems = GetItemsFromDatabase;
    }

    public IEnumerable<Item> GetItems()
    {
        return _getItems();
    }

    private IEnumerable<Item> GetItemsFromDatabase()
    {
        ...
    }
}

public class CommonStorage : Storage
{}

public class CustomStorage : Storage
{
    public CustomStorage()
    {
        _getItems = GetCustomItemsFromDatabase;
    }

    public new IEnumerable<CustomItem> GetItems()
    {
        return GetCustomItemsFromDatabase();
    }

    private IEnumerable<CustomItem> GetCustomItemsFromDatabase()
    {
        ...
    }
}

Violation of Liskov substitution principle and other problems

Initially, this section was not in the article. But I have got a lot of comments, stating that usage of 'new' keyword is a violation of Liskov substitution principle, that it just hides existing class members, that there are approaches to solve the same problem another way. I feel, that I must express my opinion about these issues.

Let me start from Liskov substitution principle. Here is what Wikipedia says about it:

Quote:

If S is a subtype of T, then objects of type T in a program may be replaced with objects of type S without altering any of the desirable properties of that program

or (from here):

Quote:

If S is a declared subtype of T, objects of type S should behave as objects of type T are expected to behave, if they are treated as objects of type T

You can read here, how 'new' keyword can violate Liskov substitution principle.

I agree, that classes in my Parent/Child examples violate this principle, but I don't agree that classes from Item example and from Storage example violate it (I'm talking about final versions of these classes, of cause). This is exactly why these protected fields in the implementations were introduced. They allow us to adhere to this principle.

In other words, I don't believe, that usage of 'new' keyword automatically means a violation of Liskov substitution principle. I think, that it can lead to the violation, and we must understand it and make certain steps to avoid it. In my article, I try to suggest one way of doing it.

I absolutely agree, that 'new' keyword hides existing methods of a base class. But sometimes this is what we need.

Here we come to another point. As it was justly mentioned by readers, sometimes there are different ways to solve the same problem. For example, our problem with storage can be solved using generics like this:

C#
public abstract class GenericStorage<T> where T : Item 
{ 
    public abstract IEnumerable<T> GetItems(); 
} 

public class CommonGenericStorage : GenericStorage<Item> 
{ 
    public override IEnumerable<Item> GetItems() 
    { 
        return GetItemsFromDatabase(); 
    } 
    
    private IEnumerable<Item> GetItemsFromDatabase() 
    { ... } 
} 

public class CustomGenericStorage : GenericStorage<CustomItem> 
{ 
    public override IEnumerable<CustomItem> GetItems() 
    { 
        return GetCustomItemsFromDatabase(); 
    } 
    
    private IEnumerable<CustomItem> GetCustomItemsFromDatabase() 
    { ... } 
} 

But here we can face other problems. Consider, that we want to write some code, which should work with any our generic repository. I can expect, that method GetItems will always return a collection, where each element is assignable to Item class. How to write code, that can work with any storage in the same way? For example, I want to have a list of all my storages. Unfortunately, the following code does not work:

C#
var storages = new List<GenericStorage<Item>> 
{ 
    new CommonGenericStorage(), 
    (GenericStorage<Item>)new CustomGenericStorage() // Compilation error 
};

In this particular case, we can solve the problem by extracting covariant interface:

C#
public interface IGenericStorage<out T> where T : Item 
{ 
    IEnumerable<T> GetItems(); 
} 

public abstract class GenericStorage<T> : IGenericStorage<T> 
    where T : Item 
{ 
    public abstract IEnumerable<T> GetItems(); 
} 

...

Now we can create our list of storages:

C#
var storages = new List<IGenericStorage<Item>> 
{ 
    new CommonGenericStorage(), 
    new CustomGenericStorage() 
};

But it is not always possible to make interface covariant.

It was my view on problems, related to the usage of 'new' keyword. I want to thank John Brett and other members of CodeProject community for their comments and discussion of the topic.

Conclusion

I hope I have managed to demonstrate, that in some cases, usage of 'new' keyword in definitions of class members can be very useful and make the interface of your classes more readable and reliable. I don't believe, that usage of 'new' keyword automatically leads to problems, but we should be aware of ways to overcome them. In any case, it is up to you to decide, which approach to use in your design.

You can read more articles on my blog.

This article was originally posted at http://ivanyakimov.blogspot.com/feeds/posts/default

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Finstek
China China
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMissing details Pin
Kirk Wood24-Aug-17 10:50
Kirk Wood24-Aug-17 10:50 
AnswerRe: Missing details Pin
Ivan Yakimov27-Aug-17 21:55
professionalIvan Yakimov27-Aug-17 21:55 
QuestionGeneric class Pin
杨夫红23-Aug-17 17:53
杨夫红23-Aug-17 17:53 
PraiseNice Pin
Charles A Peirce23-Aug-17 2:37
Charles A Peirce23-Aug-17 2:37 
GeneralRe: Nice Pin
Ivan Yakimov23-Aug-17 2:40
professionalIvan Yakimov23-Aug-17 2:40 
Question[My vote of 2] A workaround for a failed design Pin
John Brett22-Aug-17 20:44
John Brett22-Aug-17 20:44 
AnswerRe: [My vote of 2] A workaround for a failed design Pin
Ivan Yakimov23-Aug-17 4:39
professionalIvan Yakimov23-Aug-17 4:39 
GeneralRe: [My vote of 2] A workaround for a failed design Pin
John Brett23-Aug-17 22:04
John Brett23-Aug-17 22:04 
GeneralInterfaces Pin
John Brett23-Aug-17 22:23
John Brett23-Aug-17 22:23 
AnswerRe: Interfaces Pin
Ivan Yakimov27-Aug-17 21:57
professionalIvan Yakimov27-Aug-17 21:57 
GeneralRe: [My vote of 2] A workaround for a failed design Pin
stixoffire23-Aug-17 7:34
stixoffire23-Aug-17 7:34 
AnswerRe: [My vote of 2] A workaround for a failed design Pin
Richard Deeming25-Aug-17 4:45
mveRichard Deeming25-Aug-17 4:45 
Generalit is clear Pin
Southmountain22-Aug-17 6:14
Southmountain22-Aug-17 6:14 
GeneralMy vote of 4 Pin
raddevus22-Aug-17 2:11
mvaraddevus22-Aug-17 2:11 
AnswerRe: My vote of 4 Pin
Ivan Yakimov27-Aug-17 21:58
professionalIvan Yakimov27-Aug-17 21:58 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.