Functor, Lamdba and Predicate

Within this blog article I want to explain the C++ functor, lambda and predicate concepts by using them in short and easy to understand examples.

 

Functor

A functor is a class or struct which acts like a function. This is made possible by overloading the () operator. As this function is implemented by using a class, it has all the possibilities of a class. That means you can set parameters during class creation and it can hold state before and between function calls. So it is like a closure in functional languages. Furthermore a functor allows implementing another nice feature, mainly used in functional languages: higher order functions. The functor itself is a higher order function as it returns a function and it can be used in higher order functions which take one or more functions as arguments. Functors and higher order functions are commonly used in STL algorithms.

Let us start with a basic example. We have a vector with integer values and want to increase all values by a fixed or variable number. The STL offers the higher order function “for_each” which can be used to iterate over the vector and execute a function on each element. Therefore we implement a functor which is used to change an element. Within the first example we create a base functor which increases the given value by five.

struct IncreaseBy5
{
  void operator()(int& val)
  {
    val = val + 5;
  }
};

int _tmain(int argc, _TCHAR* argv[])
{  
  auto data = std::vector < int > {3,7,15};
  
  std::for_each(data.begin(), data.end(), IncreaseBy5());

	return 0;
}

 

The class IncreaseBy5 overloads the () operator and can therefore be used like a function. So you can use it within the “for_each” function of the STL.

In the next step we want to use the nice possibilities of functor classes and make our functor more universal. Therefore we allow defining the value which shall be added. This value can set during creation of the functor. The following source code shows the adapted example.

class IncreaseByX
{
public:
  IncreaseByX(int addend) : mAddend{ addend }
  {}

  void operator()(int& val)
  {
    val = val + mAddend;
  }

private:
  int mAddend;
};

int _tmain(int argc, _TCHAR* argv[])
{
  auto data = std::vector < int > {3, 7, 15};

  std::for_each(data.begin(), data.end(), IncreaseByX(10));

  return 0;
}

 

As we have heard before, this closure like concept allows us to access internal parameters during function execution. This will open a wide range of use cases. You may use the internal state of the class and the possibility of changeable class properties to define and modify the behavior of your functor.

Of course, you can also pass more parameters to the functor. The previous example with a variable value could also be implemented with a second function parameter. In such a case you must respect that the “for_each” STL function, as well as many other of STL algorithm higher order functions will accept unary functions only. Our new functor is a binary function as he has two parameters now. A unary function has one parameter only. But we can use the “bind” function to solve this issue. The bind adapter will get a function with several parameters and returns a unary function with one parameter. The following source code shows the according example.

struct IncreaseByX
{
  void operator()(int& val, int addend)
  {
    val = val + addend;
  }
};

int _tmain(int argc, _TCHAR* argv[])
{
  auto data = std::vector < int > {3, 7, 15};

  std::for_each(data.begin(), data.end(), std::bind(IncreaseByX(), std::placeholders::_1, 10));

  return 0;
}

 

STL Functors

As mentioned before, the STL already contains some basic functors. So we don’t have to reinvent the wheel and can use the existing functors. In our example application we have created a function which increases the given value. The STL already offers a nearly equivalent functor: “plus(p1,p2)” which creates the sum of the two given parameters.

With some little modification we can use the existing STL functor within our example. At first we cannot longer use the “for_each” loop because the STL function returns the result instead of use a pointer and change the value directly. Therefore we have to use the “transform” loop to modify the vector values. Furthermore, as the functor is a binary function and we need a unary one, we have to use the bind method once again.

int _tmain(int argc, _TCHAR* argv[])
{
  auto data = std::vector < int > {3, 7, 15};

  std::transform(data.begin(), data.end(), data.begin(), std::bind(std::plus<int>(), std::placeholders::_1, 10));

  return 0;
}

 

For better readability we can create the functor first and use it within the transform method.

int _tmain(int argc, _TCHAR* argv[])
{
  auto data = std::vector < int > {3, 7, 15};

  auto functor = std::bind(std::plus<int>(), std::placeholders::_1, 10);

  std::transform(data.begin(), data.end(), data.begin(), functor);

  return 0;
}

 

Use member function in higher order function

The STL higher order functions like “fore_each” or “transform” expect a functor or static member function as member. But by using “mem_fun_ref” or “mem_fun” it is possible to pass a normal member function as parameter. “mem_fun_ref” is used in case you have an object and “mem_fun” in case you have a pointer. The following example shows how to use a member function as parameter for the “for_each” method.

class Name
{
public:
  Name(std::string name) : mName(name)
  {}

  void Print()
  {
    std::cout << mName << std::endl;
  }

private:
  std::string mName;
};

int _tmain(int argc, _TCHAR* argv[])
{
  auto data = std::vector < Name >();
  data.push_back(Name("Foo"));
  data.push_back(Name("Bar"));

  std::for_each(data.begin(), data.end(), std::mem_fun_ref(&Name::Print));

  return 0;
}

 

Predicate

A predicate is a functor which returns a Boolean value. Therefore it can be used in higher order functions like “remove_if” which executes their functionality according to the return value of the given functor. Within the next example we want to remove all odd numbers from a given vector. So we create a predicte which checks whether a value is odd and use this predicate within the “remove_if” function. This function returns an iterator from start to end and removes all iterator elements matching the predicate. With this iterator we can use the erase function of the vector and delete the according elements.

class IssOddNumber
{
public:
  bool operator()(int& val)
  {
    return val % 2 ? true : false;
  }

private:
  int mAddend;
};

int _tmain(int argc, _TCHAR* argv[])
{
  auto data = std::vector < int > {3, 7, 15, 22};

  data.erase(std::remove_if(data.begin(), data.end(), IssOddNumber()), data.end());

  return 0;
}

 

Lambda functions

Last but not least I want to show how to use lambda functions to implement the functionality of the example above which increases the vector values. A lambda function is an anonymous function. You can create the function code directly where it is needed without the need of a function declaration. The following example shows how to increase the vector values by 5. Of course you can also pass additional parameters to the lambda function and add a variable value instead of the “5”.

int _tmain(int argc, _TCHAR* argv[])
{
  auto data = std::vector < int > {3, 7, 15};

  std::for_each(data.begin(), data.end(), 
    [](int& val)
    {
      val = val + 5;
    });

  return 0;
}

 

To increase the readability of the code you can create the lambda function first and use it within the for_each loop.

int _tmain(int argc, _TCHAR* argv[])
{
  auto data = std::vector < int > {3, 7, 15};

  auto IncreaseBy5 = [](int& val)
  {
    val = val + 5;
  };

  std::for_each(data.begin(), data.end(), IncreaseBy5);

  return 0;
}
Advertisements
Veröffentlicht unter C++ | Kommentar hinterlassen

Use const whenever possible

The const keyword allows you to specify a semantic constraint: an object should not be modified. And compilers will enforce that constraint. This will allow you to communicate to the compiler and to other developers that the object should remain invariant. As a result you code may become more robust, less error prone and even faster as the compiler may become more options for optimizations. Therefore, whenever possible, use the const keyword. What means „whenever possible“? Of course, as developer you know why you implement a method or a variable. So you know whether you need to change the value of a member or if it remains unchanged. And whenever you don not have to change a value you have to declare it as const. For example most method parameters are used as input only and therefore they remain unchanged and can be set to constant.
Outside of classes, you can use the const keyword at global or namespace scope, as well as for objects declared static at file, function, or block scope. Inside of classes, you can use it for both static and non-static data members. That means whenever you declare a variable you can set their behavior to const and therefore mark it as invariant.

Const pointers
Whenever you declare a pointer you have to keep in mind that two kinds of variables are involved. First you have some kind of data and second you have the pointer itself. And each of both elements can be variable or constant. Therefor you have the possibility to define the pointer and/or the data as constant:

char data[] = "Hello world"; 
char* p = data;               // non-const pointer, non-const data
const char* p = data;         // non-const pointer, const data
char* const p = data;         // const pointer, non-const data
const char* const p = data;   // const pointer, const data

Sometimes this syntax is hard to read, especially if you are new to C++ development. To make it easier to understand you can read such a pointer declaration from right to left. For example the third declaration above can be read as: you have a constant pointer p which points to a char.

Const iterators
Iterators are pointers too. So we can expect the same possibilities as above. And in fact you have the same possibilities. You can set whether the data and/or the iterator itself is constant or not. The following example shows the four possible combinations of const/non-const data and pointer in case we using an iterator for a vector.

std::vector<int> data{ 1, 2, 3 };

// non-const iterator, non-const data
std::vector<int>::iterator iter = data.begin();
*iter = 10;   // OK
++iter;       // OK

// const iterator, non-const data
const std::vector<int>::iterator iter = data.begin();   
*iter = 10;   // OK
++iter;       // error

// non-const iterator, const data
std::vector<int>::const_iterator iter = data.begin();   
*iter = 10;   // error
++iter;       // OK

// const iterator, const data
const std::vector<int>::const_iterator iter = data.begin();
*iter = 10;   // error
++iter;       // error

The iterator is a good example for the usage of const. Often you implement a loop over your container data to make data evaluations. In these cases you don’t want to change the data itself and therefore you can use “const_iterator”. But in source code I only see this type of iterator very rarely. So that is a good example why every time you declare a variable you should think about their variability.

Const Member Functions
A member functions marked as const can only be invoked at const objects. Such member functions are important because to work with const objects is a fundamental way to create efficient code. Furthermore, some people argue, that such functions make the interface of the object easier to understand. That’s true as you will now know the function does not change the object. But as the main purpose of an object is to hide the internal memory access behind an interface I would argue that such information is not relevant. If I use an object within a client I don’t want to think about the way the object works internally and in good software design I don’t have to think about this topic. The object itself is a black box. Therefore if you implement a const member function you do it because you want to write efficient code.
Typical scenarios for const member functions are getter ore data evaluation functions. The following example shows a class which holds some data and offers a function for data evaluation. This member function can be declared as const.

class MyClass
{
public:  
  bool IsEmpty() const
  {    
    return mData.empty();
  }

private:
  std::string mData;  
};

A fundamental way to improve code performance is to pass objects by reference to const. And const object instances can call const member functions only. That’s one of the reason why const member function can improve you code performance. The following code shows the previous example, extended by a non const member function and function calls. As you can see, a const object instance can call the const member functions only.

class MyClass
{
public:  
  bool IsEmpty() const
  {    
    return mData.empty();
  }

  void ChangeData()
  {
    mData = mData + "x";
  }

private:
  std::string mData;  
};

int _tmain(int argc, _TCHAR* argv[])
{
  MyClass nonConstInstance;
  const MyClass constInstance;

  nonConstInstance.ChangeData();
  nonConstInstance.IsEmpty();

  constInstance.ChangeData();   // error
  constInstance.IsEmpty();

  return 0;
}

But what does const mean? C++ has the concept of bitwise constness. Therefore compilers will check whether a const member function changes the instance data. Let us test this behavior by changing the source code of the above example and add another member function. We want to count how often the member function is called. So we add a member to store this counter and increase it on function call.

class MyClass
{
public:
  bool IsEmpty() const
  {
    mCounter++;   // error
    return mData.empty();
  }

private:
  std::string mData;
  int mCounter;
};

The compiler will check for bitwise constness and therefore an error is shown as the member mCounter cannot be modified. If we explicitly implement such functionality we can declare the according members as mutable. Mutable members can even be changed in const member functions and therefore they are not included in the bitwise constness check. The following source code shows an according implementation of the previous example.

class MyClass
{
public:
  bool IsEmpty() const
  {
    mCounter++;   // error
    return mData.empty();
  }

private:
  std::string mData;
  mutable int mCounter;
};

The possibility of mutable members speaks for my argument that a client should not think about the internal behavior of an object. Like explained previously some people argue that they like to see const functions in the object interface because they now know that the objects is not changed on such a function call. But as we have seen now, this expectation may not be true.

To make it even more complicate, there is another philosophy behind the constness of an object instance. In contrast to the bitwise constness implemented in C++ you can also think in terms of logical constness. I think it is important to think about these two perspectives. Logical constness means that the object instance is not changed from a logical point of view. As this point of view is always situational if cannot be checked by the compiler but it is an important development guideline. We have seen an according example above. Our class contains members which we explicitly want to change. So our object instance is no longer bitwise const and we explicitly have to disable this check for the according members. But from a logical point of view the object instance stays const as we only have changed some statistical data, maybe needed for debugging or code improvement reasons. Another often discussed aspect are pointers. If your calls contains pointers, you can modify the data referenced by the pointer within a const member function. As you don’t change the pointer itself the bitwise constness rule is not affected. But from a logical point of view this may have a huge impact as you change some base data of the object instance.

In summary const member functions should be used whenever possible as they will allow writing efficient code. For example a fundamental way to improve code performance is to pass objects be reference to const. And const member functions fit seamlessly into this concept. The widespread opinion that const member functions guarantee the constness of the object instance should be seen with caution. As we have seen above there are two kinds of constness. So some people speak about bitwise constness and some about logical constness. And none of these concepts will guarantee that the data of the object is not changed. Therefore the concept of object instance constness should be respected in programming concepts and guidelines. As client developer you only know the interface of the object and you should therefore not make any assumption about the object constness.

Veröffentlicht unter C++ | Kommentar hinterlassen

Dependency Inversion by using the Unity Injection Constructor

One of the five SOLID principles is Dependency Inversion. To implement according to this principle you can use interfaces and dependency injection. As a software application normally consists of hundreds or thousands of objects you will have to set up a complex tree of object instances and their dependencies. This sounds difficult but of course you can use factory methods and abstract factories and this task will become easy. Furthermore you can use dependency injection framework like Unity. These frameworks will manage the creation of object instances and their dependencies.

Within this article I want to show you a classical way to implement dependency injection with an injection constructor. And in the next step we will use Unity which will help to make the implementation a little bit cleaner.

The example application contains three layers of code. We have a management layer which uses an engine layer which uses a controller layer. The application shall calculate a price offer for a product someone wants to order. On controller level we calculate base costs for manufacturing and shipping. On engine layer we calculate the total costs. And last but not least on management layer we pay attention to product or customer specific conditions like discounts.

The complete object instance tree is created by using interfaces and dependency injection.

Let’s start with the controller layer. We create two interfaces. One for the calculation of the manufacturing costs and another one for the shipping costs.

interface IManufacturingController
{
    int CalculateManufacturingCosts(int units);
}

interface IShippingController
{
    int CalculateShippingCosts(int units);
}

And we implement according objects.

class ManufacturingController : IManufacturingController
{
    public int CalculateManufacturingCosts(int units)
    {
        return 5 * units;
    }
}

class ShippingController : IShippingController
{
    public int CalculateShippingCosts(int units)
    {
        return 10 * units;
    }
}

At next we define an engine which shall be responsible to calculate the total price.

interface IPriceEngine
{
    int GetTotalPrice(int units);
}

And we implement this engine. Within this implementation you see the concept of dependency injection by using an injection constructor.

class PriceEngine : IPriceEngine
{
    IManufacturingController _manufacturingController;
    IShippingController _shippingController;
    
    public PriceEngine(
        IManufacturingController manufacturingController,
        IShippingController shippingController)
    {
        _manufacturingController = manufacturingController;
        _shippingController = shippingController;
    }

    public int GetTotalPrice(int units)
    {
        int totalPrice = 0;

        totalPrice += _manufacturingController.CalculateManufacturingCosts(units);
        totalPrice += _shippingController.CalculateShippingCosts(units);

        return totalPrice;
    }
}

The customer manager which adds an additional discount is implemented by using the same principle. The engine is injected as constructor parameter.

interface ICustomerManager
{
    int GetPriceOffer(int units);
}

class CustomerManager : ICustomerManager
{
    IPriceEngine _priceEngine;

    public CustomerManager(IPriceEngine priceEngine)
    {
        _priceEngine = priceEngine;
    }

    public int GetPriceOffer(int units)
    {
        int offer;

        offer = _priceEngine.GetTotalPrice(units);

        if(units >= 100)
        {
            offer = (offer * 9) / 10;
        }

        return offer;
    }
}

Now we can use our manager object to calculate a price offer for the customer. The following source code shows a console application which created the needed object instances and uses the manager to calculate the offer.

static void Main(string[] args)
{
    IManufacturingController manufacturingController = new ManufacturingController();
    IShippingController shippingController = new ShippingController();

    IPriceEngine priceEngine = new PriceEngine(manufacturingController, shippingController);

    ICustomerManager customerManager = new CustomerManager(priceEngine);
            
    Console.WriteLine("offer for 200 units: " + customerManager.GetPriceOffer(200));

    Console.ReadKey();
}

As you can see it is not that difficult to create the dependency tree. But in a real application the tree will get very huge and it will contain redundant branches as you reuse components. You can negotiate this complexity and eliminate redundant creation code by using factory methods and abstract factories. But independent whether you use factories or not you have the possibility to use a dependency injection framework. One of the popular frameworks is Unity. It will allow you to create object instances and their dependencies. For example it supports the injection constructor design we have used in our example.

To use Unity you have to install the according NuGet package. At next you have to mark the injection constructors with an according attribute. The following source code shows the adaptation if the engine and manager classes.

class PriceEngine : IPriceEngine
{
    IManufacturingController _manufacturingController;
    IShippingController _shippingController;

    [InjectionConstructor]
    public PriceEngine(
        IManufacturingController manufacturingController,
        IShippingController shippingController)
    {
        _manufacturingController = manufacturingController;
        _shippingController = shippingController;
    }

    public int GetTotalPrice(int units)
    {
        ...
    }
}

class CustomerManager : ICustomerManager
{
    IPriceEngine _priceEngine;

    [InjectionConstructor]
    public CustomerManager(IPriceEngine priceEngine)
    {
        _priceEngine = priceEngine;
    }

    public int GetPriceOffer(int units)
    {
        ...
    }
}

Now we can use a unity container within the console application. We have to register all interfaces and the according classes. After this registration we can use the Unity container to create an object instance.

static void Main(string[] args)
{
    IUnityContainer unitycontainer = new UnityContainer();
    unitycontainer.RegisterType<ICustomerManager, CustomerManager>();
    unitycontainer.RegisterType<IPriceEngine, PriceEngine>();
    unitycontainer.RegisterType<IManufacturingController, ManufacturingController>();
    unitycontainer.RegisterType<IShippingController, ShippingController>();

    ICustomerManager customerManager = unitycontainer.Resolve<CustomerManager>();

    Console.WriteLine("offer for 200 units: " + customerManager.GetPriceOffer(200));

    Console.ReadKey();
}

As you can see we don’t have to know and to set the dependencies now. We have set all dependencies implicit by defining the injection constructor. Unity will use this information to create and inject all needed object instances.

Of course, Unity offers some more features and possibilities. This little example application shall give you a short introduction into the topic of Dependency Injection and will help you to decide whether you want to use Unity within your project.

Veröffentlicht unter .NET, C#, Design Pattern | Kommentar hinterlassen

Perfect Forwarding

Perfect forwarding is an implementation concept which helps avoid the forwarding problem. Therefore, if we want to understand the need for perfect forwarding, we have to start by looking at the forwarding problem. This issue can occur when you have to write a generic function that takes references as parameters and forwards these parameters to another function.

 

The forwarding problem

An easy example of a template which forwards parameters to other functions is a factory. A factory provides a generic template function which is used to create instances of different objects. The generic function will call the constructor and maybe initializer of the object to create and therefore it forwards the factory function parameters to the object functions.

In the example of this article we want to create object instances for a class to manage and draw a line. Such a line is implemented by defining a start point, a length and a direction. To keep it simple we don’t want to implement the line classes itself with all details. We want to look at the creation of object instances and therefore we use an exemplary class definition which offers a constructor only. The combination of a point, length and direction to define the line allows us to use a data structure and a simple base type for the constructor parameters.

For the point data class we will use the following object:


struct Point

{

&nbsp; Point()

&nbsp; {

&nbsp; };

&nbsp; Point(int x, int y) : X{ x }, Y{ y }

&nbsp; {

&nbsp; };

private:

&nbsp; double X;

&nbsp; double Y;

};

We assume that we have different implementations for the line object. For example they may be platform-, device- or version specific. The following code shows two different line objects.


class Line

{

public:

&nbsp; Line(Point&amp; x, int direction, int length) 

&nbsp;&nbsp;&nbsp; : mX{ x }, mDirection{ direction }, mLength{ length }

&nbsp; {

&nbsp; };

private:

&nbsp; Point mX;

&nbsp; int mDirection;

&nbsp; int mLength;

};

class OtherLine

{

public:

&nbsp; OtherLine(const Point&amp; x, const int direction, const int length) 

&nbsp;&nbsp;&nbsp; : mX{ x }, mDirection{ direction }, mLength{ length }

&nbsp; {

&nbsp; };

private:

&nbsp; Point mX;

&nbsp; int mDirection;

&nbsp; int mLength;

};

As you can see the constructors have little differences: const vs. non const parameters. As we can’t change the implementations of the line classes, our factory has to support these different use cases.

 

As we now know the existing code, we can start to implement our factory. The following code shows a possible implementation. It contains a factory class with a template function to create the line instances. Furthermore we create a console application which will use the factory to create line instances. The factory is called several times, each time with a little bit different combination of parameters (members vs. temporary objects and integer literals).


class Factory

{

public:

&nbsp; template&lt;typename T, typename A, typename B, typename C&gt;

&nbsp; static T CreateModule(A x, B direction, C length)

&nbsp; {

&nbsp;&nbsp;&nbsp; return T(x, direction, length);

&nbsp; }

};

int _tmain(int argc, _TCHAR* argv[])

{

&nbsp; Point x{ 4, 7 };

&nbsp; int i{ 6 };

&nbsp; Factory::CreateModule&lt;Line&gt;(x, i, i);

&nbsp; Factory::CreateModule&lt;Line&gt;(x, i, 5);

&nbsp; Factory::CreateModule&lt;Line&gt;(x, 5, 5);

&nbsp; Factory::CreateModule&lt;Line&gt;(Point(), i, i);

&nbsp; Factory::CreateModule&lt;Line&gt;(Point(), i, 5);

&nbsp; Factory::CreateModule&lt;Line&gt;(Point(), 5, 5);

&nbsp; Factory::CreateModule&lt;OtherLine&gt;(x, i, i);

&nbsp; Factory::CreateModule&lt;OtherLine&gt;(x, i, 5);

&nbsp; Factory::CreateModule&lt;OtherLine&gt;(x, 5, 5);

&nbsp; Factory::CreateModule&lt;OtherLine&gt;(Point(), i, i);

&nbsp; Factory::CreateModule&lt;OtherLine&gt;(Point(), i, 5);

&nbsp; Factory::CreateModule&lt;OtherLine&gt;(Point(), 5, 5);

&nbsp; return 0;

}

So we are done. The factory is implemented and works fine for the different use cases. So where is the forwarding problem? I didn’t occur yet because we have implemented a factory which copies the function parameters and don’t forward it. Therefore the factory will work fine but you may get performance issues, especially if you have to use large data structures and not such simple ones like the point. Therefore, as next step, we want to forward the parameters instead of copy them. So we modify the factory function a little bit and pass the parameters as reference.


class Factory

{

public:

&nbsp; template&lt;typename T, typename A, typename B, typename C&gt;

&nbsp; static T CreateModule(A&amp; x, B&amp; direction, C&amp; length)

&nbsp;&nbsp;{

&nbsp;&nbsp;&nbsp; return T(x, direction, length);

&nbsp; }

};

int _tmain(int argc, _TCHAR* argv[])

{

&nbsp; Point x{ 4, 7 };

&nbsp; int i{ 6 };

&nbsp; Factory::CreateModule&lt;Line&gt;(x, i, i);

&nbsp; Factory::CreateModule&lt;Line&gt;(x, i, 5);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // error

&nbsp; Factory::CreateModule&lt;Line&gt;(x, 5, 5);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // error

&nbsp; Factory::CreateModule&lt;Line&gt;(Point(), i, i);

&nbsp; Factory::CreateModule&lt;Line&gt;(Point(), i, 5);&nbsp;&nbsp; // error

&nbsp; Factory::CreateModule&lt;Line&gt;(Point(), 5, 5);&nbsp;&nbsp; // error

&nbsp; Factory::CreateModule&lt;OtherLine&gt;(x, i, i);

&nbsp; Factory::CreateModule&lt;OtherLine&gt;(x, i, 5);&nbsp;&nbsp;&nbsp; // error

&nbsp; Factory::CreateModule&lt;OtherLine&gt;(x, 5, 5);&nbsp;&nbsp;&nbsp; // error

&nbsp; Factory::CreateModule&lt;OtherLine&gt;(Point(), i, i);

&nbsp; Factory::CreateModule&lt;OtherLine&gt;(Point(), i, 5);&nbsp; // error

&nbsp; Factory::CreateModule&lt;OtherLine&gt;(Point(), 5, 5);&nbsp; // error

&nbsp; return 0;

}

Unfortunately this will result in errors. The function calls using rvalues will not work any longer. For example it is not possible to pass the integer literal as reference. But with another modification we may solve this issue. So let us try to change the factory function again and pass the parameters as const reference.


class Factory

{

public:

&nbsp; template&lt;typename T, typename A, typename B, typename C&gt;

&nbsp; static T CreateModule(const A&amp; x, const B&amp; direction, const C&amp; length)

&nbsp; {

&nbsp;&nbsp;&nbsp; return T(x, direction, length);&nbsp;&nbsp; // error

&nbsp; }

};

int _tmain(int argc, _TCHAR* argv[])

{

&nbsp; Point x{ 4, 7 };

&nbsp; int i{ 6 };

&nbsp; Factory::CreateModule&lt;Line&gt;(x, i, i);

&nbsp; Factory::CreateModule&lt;Line&gt;(x, i, 5); 

&nbsp; Factory::CreateModule&lt;Line&gt;(x, 5, 5); 

&nbsp; Factory::CreateModule&lt;Line&gt;(Point(), i, i);

&nbsp; Factory::CreateModule&lt;Line&gt;(Point(), i, 5);

&nbsp; Factory::CreateModule&lt;Line&gt;(Point(), 5, 5);

&nbsp; Factory::CreateModule&lt;OtherLine&gt;(x, i, i);

&nbsp; Factory::CreateModule&lt;OtherLine&gt;(x, i, 5); 

&nbsp; Factory::CreateModule&lt;OtherLine&gt;(x, 5, 5);&nbsp;&nbsp; 

&nbsp; Factory::CreateModule&lt;OtherLine&gt;(Point(), i, i);

&nbsp; Factory::CreateModule&lt;OtherLine&gt;(Point(), i, 5);&nbsp; 

&nbsp; Factory::CreateModule&lt;OtherLine&gt;(Point(), 5, 5);&nbsp; 

&nbsp; return 0;

}

Now the factory function call will work but the factory can no longer call the non const line constructors. One possible solution is overloading. You may provide overloaded versions of the factory function with const and non const parameters. Of course, with increasing number of parameters the number of possible parameter combinations will grow exponentially. And of course this procedure contradicts our goal to implement a single template function.

 

Now we see the forwarding problem and we can summarize it with the following explanation. The forwarding problem can occur when you write a generic function that takes references as its parameters forwards these parameters to another function. If the generic function takes a parameter of type T&, then the function cannot be called by using an rvalue. If the generic function takes a parameter of type const T&, then the called function cannot modify the value of that parameter.

 

Perfect forwarding

As we now have seen the forwarding problem we will try to solve this issue and implement perfect forwarding within our factory template function. This will be possible by using rvalue references parameters. They enable us to write one template function which accepts const and non const arguments and forwards them to another function as if the other function had been called directly. For more details about the concept of lvalues, rvalues and rvalue references you may read my previous article.

We can use the rvalue referenace declaratory and adapt our template function.


template&lt;typename T, typename A, typename B, typename C&gt;

static T CreateModule(A&amp;&amp; x, B&amp;&amp; direction, C&amp;&amp; length)

{

&nbsp; ...

}

To understand this template we have to know two C++ rules: type deduction and reference collapsing.

 

Type deduction

In case of a template function T&& is not an rvalue reference. When the function is instantiated, T depends on whether the argument passed to the function is an lvalue or an rvalue. If it’s an lvalue of type U, T is deduced to U&. If it’s an rvalue, T is deduced to U. This rule may seem unusual but it starts making sense when we realize it was designed to solve the perfect forwarding problem.

 

Reference collapsing

The other rule is reference collapsing. Taking a reference to a reference is illegal in C++. However, it can sometimes arise in the context of templates and type deduction. On template instantiation there may be types like “int& &” or “int& &&”. While this is something which you cannot write in code the compiler will accept such template instantiations and infers a single reference from this. The reference collapsing rule is defined for this use case. The rule simply says that “&” always wins and the only case where “&&” results is “&& &&”. Following you will see all possible cases and the result after using the reference collapsing rule.

  • “& &” à “&”
  • “& &&” à “&”
  • “&& &” à “&”
  • “&& &&” à “&&”

 

Forwarding References or Universal References

As we have seen the two new rules we will now understand the rvalue reference within the deducing context. As this type of reference is different from a standard rvalue reference a new term was introduced: universal reference. This term was introduced by Scott Meyers because he clearly want to differentiate between true rvalue references and something what looks like an rvalue reference but might end up being an lvalue reference.

Later, several members of the C++ standard committee acknowledged the fact that there is a need to name the T&& references. So they came up with the term forwarding reference. The proposal for this change explains why the name forwarding reference is preferred over universal reference.

 

std::forward

After this short excursion we want to come back to our template function implementation. As we now understand forwarding references we know that they can be an rvalue or an lvalue reference. To forward this reference to a method the std::forward function was introduced. This function perfectly forwards each parameter either as an rvalue or as an lvalue, depending on how it was passed in.

So we can use the std::forward function to pass our parameters to the object constructor. Following you will see the final factory function of our example and the according use of the factory within the console application.


class Factory

{

public:

&nbsp; template&lt;typename T, typename A, typename B, typename C&gt;

&nbsp; static T CreateModule(A&amp;&amp; x, B&amp;&amp; direction, C&amp;&amp; length)

&nbsp; {

&nbsp;&nbsp;&nbsp; return T(std::forward&lt;A&gt;(x), std::forward&lt;B&gt;(direction), std::forward&lt;C&gt;(length));

&nbsp; }

};

int _tmain(int argc, _TCHAR* argv[])

{

&nbsp; Point x{ 4, 7 };

&nbsp; int i{ 6 };

&nbsp; Factory::CreateModule&lt;Line&gt;(x, i, i);

&nbsp; Factory::CreateModule&lt;Line&gt;(x, i, 5);

&nbsp; Factory::CreateModule&lt;Line&gt;(x, 5, 5);

&nbsp; Factory::CreateModule&lt;Line&gt;(Point(), i, i);

&nbsp; Factory::CreateModule&lt;Line&gt;(Point(), i, 5);

&nbsp; Factory::CreateModule&lt;Line&gt;(Point(), 5, 5);

&nbsp; Factory::CreateModule&lt;OtherLine&gt;(x, i, i);

&nbsp; Factory::CreateModule&lt;OtherLine&gt;(x, i, 5);

&nbsp; Factory::CreateModule&lt;OtherLine&gt;(x, 5, 5);

&nbsp; Factory::CreateModule&lt;OtherLine&gt;(Point(), i, i);

&nbsp; Factory::CreateModule&lt;OtherLine&gt;(Point(), i, 5);

&nbsp; Factory::CreateModule&lt;OtherLine&gt;(Point(), 5, 5);

&nbsp; return 0;

}

Perfect forwarding within the C++ standard

The standard libraries make use of perfect forwarding. For example the vector object as well as other containers offer the emplace_back function as alternative to push_back. In case of push_back a temporary object instance is created. This means it is necessary to construct, move and destruct the temporary object. If you use emplace_back instead, the object will be created directly within the vector without the need of a temporary object. This is possible because the function parameter passed to emplace_back us passed by using perfect forwarding.

 

Summary

The concept of perfect forwarding allows you to write efficient code which is executed without the need to create, move and destruct temporary objects. Perfect forwarding is based on forwarding references which use the concepts of type deduction and reference collapsing. These concepts are part of the language and so you can easily write functions using perfect forwarding.

Veröffentlicht unter C++ | Kommentar hinterlassen

Scoped vs. unscoped enum

C++ has two kinds of enumerators. You will find several names for both. Following you will see the two kinds of enumerators and some of their names:

  • Standard enum / plain enum / unscoped enum / conventional enum
  • Strong enum / enum class / scoped enum / new enum / managed enum

 

I think none of the names is perfect because if you are not familiar with the differences of both enumerators all of the names can result in misinterpretations.

I prefer the names standard and strong enum and will therefore use them within this article. As I will explain later on within the article I also like to call them scoped and unscoped enum.

 

Standard enum

The following example shows the creation and usage of a standard enumerator.

namespace MyNamespace
{
  enum Colors
  {
    Red,
    Green,
    Blue
  };
}

using namespace MyNamespace;

int _tmain(int argc, _TCHAR* argv[])
{
  Colors color = Red;
  int enumValue = Colors::Red;

  enumValue = Red + Blue;

  return 0;
}

 

Normally you will implement your applications in different files and therefore the enumerator definition is often separated from its usage. To keep it simple the above example and all following examples are implemented in one file only but with respect to the normal use case of separation in different files. Therefore you will find the uncommon definition of a namespace followed by a using command right before the main method.

The main method shows how you can create an enumerator value. Furthermore it contains examples to demonstrate one of the disadvantages of the standard enumerator. You can access the underlying type directly. Therefore it is possible to create an integer value by an enumerator and it is even possible to do calculations with the enumerator values.

A second issue can be seen in case we add another enumerator. Enumerator names are in the same scope as the enum definition. Therefore “Red”, “Green” and “Blue” are within the “MyNamespace” scope. The following source code shows what happens if we add another enumerator which contains an already used name: a name collision will occur and the compiler reports an error.

namespace MyNamespace
{
  enum Colors
  {
    Red,
    Green,
    Blue
  };

  enum OtherColors
  {
    Yellow,
    Blue,   // error
  };
}

using namespace MyNamespace;

int _tmain(int argc, _TCHAR* argv[])
{
	return 0;
}

 

That’s why the standard enumerator is also called unscoped. The values of an enumeration exist in whatever scope it was declared in.

One possible and often seen solution is to extend the name with a short prefix or with the full enumeration name.

namespace MyNamespace
{
  enum Colors
  {
    ColorsRed,
    ColorsGreen,
    ColorsBlue
  };

  enum OtherColors
  {
    OtherColorsYellow,
    OtherColorsBlue,
  };
}

using namespace MyNamespace;

int _tmain(int argc, _TCHAR* argv[])
{
  Colors color = ColorsBlue;
  OtherColors otherColors = OtherColorsBlue;

  return 0;
}

 

This will work but it reduces the readability of the source code. Another possibility is to use different namespaces. I like to use the following kind of template:

  • Put each enumerator in its own Namespace
  • Use the enumeration name as namespace name
  • Use “Enum” as enumeration name

By using this template the previous can be changed to:

namespace Colors
{
  enum Enum
  {
    Red,
    Green,
    Blue
  };
}

namespace OtherColors
{
  enum Enum
  {
    Yellow,
    Blue,
  };
}

int _tmain(int argc, _TCHAR* argv[])
{
  Colors::Enum color = Colors::Blue;
  OtherColors::Enum otherColors = OtherColors::Blue;

  return 0;
}

 

I really like this template because it creates very clean code. If you declare a variable, a function parameter or a function result you will name it explicitly as “Enum” and on use of the enum you access the values with by using the namespace which contains a nice name for the enumeration. So you get clean code, ideal scoping and it does not affect the runtime or compilation times.

 

Strong enum

C++ offers a second kind of enumerators. This strong enum can be declared by writing “enum class”. In contrast to the standard enum the strong one is scoped. Therefore you don’t have to fear conflicts if you use the same names for enumerator values. The following code shows the adapted example and this time we can add both enums to one namespace.

namespace MyNamespace
{
  enum class Colors
  {
    Red,
    Green,
    Blue
  };

  enum class OtherColors
  {
    Yellow,
    Blue,
  };
}

using namespace MyNamespace;

int _tmain(int argc, _TCHAR* argv[])
{
  Colors color = Colors::Blue;
  OtherColors otherColors = OtherColors::Blue;

  int enumValue = Colors::Red;  // error

  return 0;
}

 

Within the main method I have created two variables and initialized them with enumerator values. As you can see the values are accessed by using the according scope. Furthermore the main method shows another nice concept of the strong enumerator. It is strongly typed and therefore you cannot assign its values to an integer variable. The compiler will show an according error message for this assignment. This prevents accidental misuse of constants. The strong enum is an “enum class” because it combines the traditional enumeration aspects with the features of classes.

 

Advantages of enum classes (part 1)

Within the C++11 FAQ Bjarne Stroustrup names the following advantages of strong enums:

  • conventional enums implicitly convert to int, causing errors when someone does not want an enumeration to act as an integer.
  • conventional enums export their enumerators to the surrounding scope, causing name clashes.
  • the underlying type of an enum cannot be specified, causing confusion, compatibility problems, and makes forward declaration impossible.

This is a nice summary of the advantages and I think it is worth to have a deeper look into some aspects.

 

The underlying type of an enum

What bothers me with enums is the focus on the underlying type. Two of the three points from Bjarne Stroustrup contain topics regarding the underlying type of the enum. From my point of view these advantages are insignificant because I think they don’t reflect the use case for enums. I think to use the underlying value of enums, compare enums (, =) or even calculate with enums (a+b) is bad coding style, no matter whether you use standard or strong enums (with type casting).

Normally we create an enum because we want to represent a set of values belonging together, for example the four cardinal points. Therefore the enums values are grouped identifiers. Their main purpose is to increase the code readability and quality. As developer you want to write your code by using the enum identifers and you should never waste a thought whether the underlying type is an integer or a char or something else. Therefore never ever quantify enums or calculate with enums.

Of course there may be exceptions to this rule. For example if you want to optimize your code to get the highest possible execution performance, you may have to use standard enums and even use their underlying types. With such kind of optimizations you will explicitly move your quality criteria away from things like readability to performance. In such scenarios where you explicitly want to use the underlying type I would recommend to directly create variables of this type and don’t use enums. In such cases you can write your high performance code without the use of enums and offer a scoped enum at higher level, for example within you API.

Therefore we don’t want to look at such rare use cases. In standard use cases you have to use an enum by its identifier only and never use the underlying type. If you have to write an API which offers integer values instead of the enums (e.g. for compatibility reasons) you should write explicit converter functions which converts the enum from and to an integer. This will highly increase the readability and maintainability of your code.

 

Advantages of enum classes (part 2)

With this thought in mind we can come back to the advantages of strong enums. By following the above guideline and never use the underlying type we can filter out these advantages from Bjarne Stroustrup’s list. As result the following two advantages of strong enums remain:

  • strong enums are scoped
  • strong enums allow forward declaration

 

We have seen the advantage of scoping within the example above so we now want to have a look at the forwarding feature. The following code shows a typical use case. The enumerator is declared in one file. Within another file you declare your class interface and want to use the enumerator for example for method parameters or return values. And in a third file you implement and use your class interface. As the strong enum allows forward declaration you can use this feature within the file which contains you class interface. You will see the according forward declaration within the following source code.

// header file with interface declaration

enum class Colors;

Colors GetBackgroundColor();

// file with enum definitions

enum class Colors
{
  Red,
  Green,
  Blue
};

// source file with interface definition

Colors GetBackgroundColor()
{
  return Colors::Blue;
}

int _tmain(int argc, _TCHAR* argv[])
{
  Colors color = GetBackgroundColor();

  return 0;
}

 

Within my projects I had to deal more often with enumerator scoping conflicts than with the need for forward declaration. Therefore I think the fact that the strong enums are scoped is their main advantage. That’s why the title of this article is “scoped vs. unscoped enum”. And as I told you at the beginning of the article I also like to call the enums soped and unscoped instead of standard and strong.

 

Summary

The strong enum offers some really nice advantages compared to the standard enum. The most importand one is the scoping feature, followed by the support of forward declaration. Therefore you should prefer strong enums.

If you have to use standard enum (e.g. in legacy code) you can increase the readability of your code by using the namespace pattern shown above.

No matter if you use standard enums or strong enums you should always use them as grouped identifiers. If there is a need to use the underlying type then this is an indication that you don’t want to use an enum at all. In this case it is better to create variables of the underlying type and offer the enumerator in the API only. In this case the conversion from the enumerator use in the API to the internal type shall be done with explicitly implemented converter methods which will not access the underlying type of the enumerator.

Veröffentlicht unter C++ | Kommentar hinterlassen

Does testing affect software quality?

Within software development projects you may often heard questions or statements like these:

“We are in trouble with the timelines so we have to skip some of the software tests. How will this affect the software quality?”

“Our software quality is low. Can we add some software tests to increase the quality?”

 

These are typical questions. But I think there is a general misunderstanding between the relationship of software tests and software quality. Maybe you can ask yourself: Is there a relationship between these two attributes and if yes what kind of relationship? As you will see, this question is very difficult to answer.

Quality does not mean testing! There are some managers and even developers who think in this way but to set these two attributes into a strict relationship will not work. Please, don’t understand me wrong. There may be a relationship but to say quality is equal to or only depends on testing is wrong. But what if we weaken the connection between these two attributes a little bit? If we do so, it will become difficult to find a concreate statement whether testing affects quality or not. I know there are many different opinions regarding this question. And of course there is no final answer. So this article will show my opinion and should give you some ideas and thoughts regarding this topic.

 

What is a Feature?

At first I want to have a look on another software attribute beside the quality: the software feature. A feature realization is always a cyclic execution of implementation and testing. To implement a feature, its requirements and therefore the expected use cases will be defined. The implementation itself always contains coding and testing. These two steps will be done in few cycles until the feature is finished. Therefore testing is an integral part of feature implementation. It ensures that the software feature will fit with the specified behavior and can be used by the customer. Without testing the feature will never ever work in all expected use cases.

Of course, software will never be without bugs, but if a feature is realized you should be sure it will work in normal cases. In special cases it may result in errors as not all special cases are tested. The difference between “normal” and “special” cases is: “normal” use cases cover the specified functionality of the feature and “special” use cases will use the feature in a way which is outside of the specification. If I talk about tests I mean the tests to check the expected and specified functionality of the feature. If you skip such tests, the feature will not work in some or many of the expected cases. Therefore if you skip tests you reduce the feature set of your release and not the quality.

 

What is Quality?

There are many definitions for quality. I like the common definitions which are independent of the kind of product and not specific for software development. A simple definition is: “Quality is the grade how requirements are meet”. This means the requirement must be fulfilled and quality adds some additional benefit. So another good definition is: “Quality is the sum of customer benefit beside the functionality of the product.”

What does this mean in terms of software? In software projects we normally have two main types of customers: the end user and the product owner. Sometimes these may be the same persons. In most cases the product owner is the company you are working for and the end users are the persons which buy and use the software.

Quality for the end user is focused on using the software. There may be attributes like: reliability, usability and efficiency. For example the wish “The software must be easy to use” is a typical quality criteria, except the user guidance in the software is specified in a detailed manner and therefore is implemented as feature.

Quality for the product owner is focused on further developing of the software. In this case there are attributes like: changeability, maintenance, flexibility and documentation. So the quality from product owner point of view is mainly focused on the code quality.

 

Does testing affect software quality?

Now, as we found a definition for the term “Quality” we will come back to our initial question: “Does testing affect software quality?” As mentioned before, “standard testing” means check the feature specific use cases. This kind of testing will not increase the quality of the product as it is not focused on quality criteria’s. It is focused on the feature.

But what if we do additional tests? That means, test which are independent of the feature specific standard tests. Can we increase the product quality with such tests?

As said on the beginning of the article: Skip testing does not decrease the software quality it removes features or parts of features. And on the other hand we now think about the possibility to increase the quality with additional test. At first this may sound like a contradiction. But you have to separate the different kinds of testing and look at their goals. The feature related tests are needed to implement the features. The additional tests are done to check things outside of the main functionality. So they are done to check the possible customer benefit beside the functionality of the product. And that exactly matches with our definition of quality.

Such additional testing outside of the normal use cases will therefore increase the quality of the product. Really? The tests itself will help to evaluate the software quality. At next the product owner has to decide whether the software should be changed or not. Therefore the additional tests focused on quality are an entry point for quality changes. So these tests are similar to end user feedbacks and will help to increase the quality.

 

Summary

There are different points of view whether testing is a quality attribute or not. This depends on the kind of test you speak about. If someone speaks about software tests, in most cases the tests to check the intended behavior of a feature are meant. In my opinion such tests are part of the feature development itself. So these tests do not influence the software quality.

As conclusion, in case a product owner wants to reduce testing effort, you have to look carefully on the consequences and explain them in terms of feature loss (most often) and/or quality loss (rarely).

 

Veröffentlicht unter Projektleitung | Kommentar hinterlassen

Linq vs Loop: Join

Like in the previous article of this series I want to compare Linq with a classical loop. This time we want to look at data objects which shall be joined to create a result. Again we want to use validated clean data as input and raw data including null pointers.

Use case one: clean data

Let’s start with the first use case. We have a simple data class for a person and a data class for an address. The data classes are linked together by the AddressIdentifier property.

Out of a list of persons and addresses we want to find a specific person by name. The result shall contain the person name and address. To keep it simple we just look for the first person with the name. If the list does not contain the data we are looking for, we shall return a default person and address.

The data classes are defined as following:

public class Person
{        
    public string Name { get; set; }
    public uint Age { get; set; }
    public uint AddressIdentifier { get; set; }

    public static readonly Person Default = new Person()
    {
        Name = "new person",
        Age = 0,
        AddressIdentifier = 0
    };        
}

public class Address
{
    public uint AddressIdentifier { get; set; }

    public string City { get; set; }

    public static readonly Address Default = new Address()
    {
        AddressIdentifier = 0,
        City = "new city"            
    };
}

 

Our demo console application creates a list of data and calls the data query method, first with an existing person and second with a not existing one.

List<Person> persons = new List<Person>();

persons.Add(new Person() { Name = "John Doe", Age = 35, AddressIdentifier = 1 });
persons.Add(new Person() { Name = "Jane Doe", Age = 41, AddressIdentifier = 1 });

List<Address> addresses = new List<Address>();
addresses.Add(new Address() { AddressIdentifier = 1, City = "Chicago" });

//---------

string information;

//search existing person
information = GetPersonInformation(persons, addresses, "Jane Doe");
Console.WriteLine(information);

//search not existing person
information = GetPersonInformation(persons, addresses, "???");
Console.WriteLine(information);

Console.ReadKey();

The data query method shall be implemented twice: by using a loop and by using Linq. We start with the classical loop:

static private string GetPersonInformation(List<Person> persons, List<Address> addresses, string name)
{
    Person actualPerson = Person.Default;
    Address actualAddress = Address.Default;

    foreach (Person person in persons)
    {
        if (string.Equals(person.Name, name, StringComparison.OrdinalIgnoreCase))
        {
            actualPerson = person;
            break;
        }
    }

    foreach (Address address in addresses)
    {
        if (actualPerson.AddressIdentifier == address.AddressIdentifier)
        {
            actualAddress = address;
            break;
        }
    }

    return actualPerson.Name + ", " + actualAddress.City;
}

And we implement the same function by using Linq.

static private string GetPersonInformation(List<Person> persons, List<Address> addresses, string name)
{
    var result = from person in persons
                    join address in addresses
                    on person.AddressIdentifier equals address.AddressIdentifier
                    where string.Equals(person.Name, name, StringComparison.OrdinalIgnoreCase)
                    select new
                    {
                        Name = person.Name,
                        City = address.City
                    };

    var element = result
        .DefaultIfEmpty(new { Name = Person.Default.Name, City = Address.Default.City })
        .First();

    return element.Name + ", " + element.City;
}

 

Code Review for use case one

The query using the loops is easy to understand and contains clean code. You may think about the possibility to extract the both loops and create single methods to find a person and an address. By doing this refactoring you create three very short and simple methods but with a little increase in complexity. Therefore in my opinion a single method with both loops is fine too.

The Linq query is easy to understand too. You have to know some details about Linq, for example the need for the DefaultIfEmpty statement may not be clear in the first moment. Therefore it would be helpful to add some comments to the query to explain why some statements are needed.

I don’t favor any of the two implementations. From my point of view they are coequal.

Use case two: dirty data

The second use case adds an important need: the query must be robust. So the data may for example contain null values. Like in the first use case the method shall return default data if the person we looking for is not found. Null values or not initialized list shall not throw an error. In this case also the default data shall be returned.

In our test console application we create some dirty data. And we add additional tests to call the function with the data or even with null parameters.

List<Person> persons = new List<Person>();

persons.Add(new Person() { Name = "John Doe", Age = 35, AddressIdentifier = 1 });
persons.Add(null);
persons.Add(new Person() { Name = null, Age = 38, AddressIdentifier = 2 });
persons.Add(new Person() { Name = "Jane Doe", Age = 41, AddressIdentifier = 3 });
persons.Add(new Person() { Name = "Jane Foe", Age = 41, AddressIdentifier = 4 });

List<Address> addresses = new List<Address>();
addresses.Add(new Address() { AddressIdentifier = 1, City = "Chicago" });
addresses.Add(new Address() { AddressIdentifier = 2, City = null });
addresses.Add(null);
addresses.Add(new Address() { AddressIdentifier = 3, City = "Chicago" });            

//---------

string information;

//search existing person
information = GetPersonInformation(persons, addresses, "Jane Doe");
Console.WriteLine(information);

information = GetPersonInformation(persons, addresses, "Jane Foe");
Console.WriteLine(information);

//search not existing person
information = GetPersonInformation(persons, addresses, "???");
Console.WriteLine(information);

//search in a list which is not yet initialized
information = GetPersonInformation(null, addresses, "???");
Console.WriteLine(information);

information = GetPersonInformation(persons, null, "???");
Console.WriteLine(information);

information = GetPersonInformation(null, null, "???");
Console.WriteLine(information);

Console.ReadKey();  

The implemented query using the loop must be adapted to handle all these special cases. The following source code shows an according implementation. The list and the list content will be checked for null values.

static private string GetPersonInformation(List<Person> persons, List<Address> addresses, string name)
{
    Person actualPerson = Person.Default;
    Address actualAddress = Address.Default;

    if (persons != null)
    {
        foreach (Person person in persons)
        {
            if (person == null)
            {
                continue;
            }

            if (string.Equals(person.Name, name, StringComparison.OrdinalIgnoreCase))
            {
                actualPerson = person;
                break;
            }
        }
    }

    if (addresses != null)
    {
        foreach (Address address in addresses)
        {
            if (address == null)
            {
                continue;
            }

            if (actualPerson.AddressIdentifier == address.AddressIdentifier)
            {
                actualAddress = address;
                break;
            }
        }
    }

    return actualPerson.Name + ", " + actualAddress.City;
}

 

The implementation of the Linq query must be adapted too. A check of the whole list, as well of the single element is added.

static private string GetPersonInformation(List<Person> persons, List<Address> addresses, string name)
{
    if(persons == null)
    {
        persons = new List<Person>();
    }

    if(addresses == null)
    {
        addresses = new List<Address>();
    }

    var result = from person in persons.Where(p => p != null)
                    join address in addresses.Where(a => a != null) 
                    on person.AddressIdentifier equals address.AddressIdentifier                         
                    where string.Equals(person.Name, name, StringComparison.OrdinalIgnoreCase)
                    select new
                    {
                        Name = person.Name,
                        City = address.City
                    };

    var element = result
        .DefaultIfEmpty(new { Name = Person.Default.Name, City = Address.Default.City })
        .First();

    return element.Name + ", " + element.City;
}      

Code Review for use case two

The method containing the loops gets more complex with all the if-statements. Therefore you should extract the two loops and create single methods looking for a person and an address. By doing this little refactoring the loop implementation will become very easy to understand.

The Linq implementation was not changed much. Before execution of the query some data checks are done. But there is a little detail, the additional small queries within the in-parts. These queries are needed to remove null objects. I think you have some possibilities to refactor this implementation. You may extract the nested queries or the data checks. Or in case you want to leave the complex query as it is, you should add comments to explain it a little bit.

Without refactoring I don’t like any of these two implementations as they are some kind of complex. I would like to have two separate query methos, one for the person and the other one for the address and an additional managing method which calls these two query methods and joins the result. The single query methods as well as the join can be implemented with simple Linq statements.

Veröffentlicht unter .NET, C#, LINQ | Kommentar hinterlassen