[Canonical page](<https://docs.dhtmlx.com/planning-engine/net/api/core/evaluate/candidate-result/>)
[Full documentation index](<https://docs.dhtmlx.com/planning-engine/llms.txt>)


# CandidateResult

The score of one [`Candidate`](<https://docs.dhtmlx.com/planning-engine/net/api/core/evaluate/candidate.md>). [`Project.Evaluate`](<https://docs.dhtmlx.com/planning-engine/net/api/project-evaluate.md>) and [`Project.EvaluateWith`](<https://docs.dhtmlx.com/planning-engine/net/api/project-evaluate.md#evaluatewith>) return one per candidate in [`EvaluateResult.Candidates`](<https://docs.dhtmlx.com/planning-engine/net/api/core/evaluate/evaluate-result.md>), in input order.

## Declaration

```csharp
namespace Plen;

public sealed class CandidateResult : IDeepCloneable<CandidateResult>, IEquatable<CandidateResult>
```

A result holds either an error or metrics, never both. Check `Error` first.

## Properties

| Property | Type | Description |
|---|---|---|
| `Id` | [`Id`](<https://docs.dhtmlx.com/planning-engine/net/api/core/data/id.md>) | The candidate's `Id`, copied unchanged. `required`. |
| `Error` | [`RejectedOperation?`](<https://docs.dhtmlx.com/planning-engine/net/api/core/changes/rejected-operation.md>) | Set when the candidate failed: the first rejected operation of its change set, or an input error from its solve or level run. `Path` is empty when the error has no input path. When set, `Scheduled`, `Leveled` and `Cost` are `null`. |
| `Scheduled` | [`Metrics?`](<https://docs.dhtmlx.com/planning-engine/net/api/core/evaluate/metrics.md>) | Metrics of the scheduled stage. Present on every candidate without `Error`. |
| `Leveled` | [`LeveledMetrics?`](<https://docs.dhtmlx.com/planning-engine/net/api/core/evaluate/leveled-metrics.md>) | Metrics of the leveled stage. Present only when [`EvaluateOptions.Level`](<https://docs.dhtmlx.com/planning-engine/net/api/core/evaluate/evaluate-options.md>) is set and the scheduled stage has no cycles. |
| `Cost` | `double?` | The cost of the last stage present: `Leveled.Metrics.Cost` when `Leveled` is set, else `Scheduled.Cost`. Lower is better. `null` when `Error` is set. |

For how cost is computed, see [`Metrics.Cost`](<https://docs.dhtmlx.com/planning-engine/net/api/core/evaluate/metrics.md>).

## Methods

### DeepClone

```csharp
public CandidateResult DeepClone()
```

Returns a deep copy. No list or nested object is shared with the original.

### Equals

```csharp
public bool Equals(CandidateResult? other)
public override bool Equals(object? obj)
```

Value equality: compares `Id`, `Error`, `Scheduled`, `Leveled` and `Cost` field by field, with nested lists compared element by element. `Cost` compares with `==`. `GetHashCode` is consistent with it.

### ToString

```csharp
public override string ToString()
```

Returns a readable rendering of all properties, for logging.

## Example

Rank the scored candidates by cost and list the rejected ones separately.

```csharp
using Plen;

var monday = Moment.FromYmdHm(2026, 8, 10, 8, 0)
    ?? throw new InvalidOperationException("invalid date");

var data = new ProjectData
{
    Tasks = [new TaskData(1) { StartDate = monday, Duration = 2.0 }],
    Resources = [new ResourceData { Id = "dev1" }, new ResourceData { Id = "dev2" }],
};

var project = new Project();
project.Solve(data, new SolveOptions());

var candidates = new List<Candidate>();
foreach (var dev in new[] { "dev1", "dev2" })
{
    var changes = new SchedulingChanges();
    changes.Assignments.Add.Add(new AssignmentData { Task = 1, Resource = dev, Value = 1.0 });
    candidates.Add(new Candidate { Id = dev, Changes = changes });
}

// Rejected: "dev9" isn't in the project.
var bad = new SchedulingChanges();
bad.Resources.Remove.Add("dev9");
candidates.Add(new Candidate { Id = "drop-dev9", Changes = bad });

var result = project.Evaluate(candidates, new EvaluateOptions());

foreach (var c in result.Candidates.Where(c => c.Error is null).OrderBy(c => c.Cost))
    Console.WriteLine($"{c.Id}: cost {c.Cost}, conflicts {c.Scheduled!.Conflicts.Value}");

foreach (var c in result.Candidates.Where(c => c.Error is not null))
    Console.WriteLine($"{c.Id} rejected at {c.Error!.Path}: {c.Error.Message}");
```

## Related

- [`Project.Evaluate`](<https://docs.dhtmlx.com/planning-engine/net/api/project-evaluate.md>) - the call that scores the candidates
- [`Candidate`](<https://docs.dhtmlx.com/planning-engine/net/api/core/evaluate/candidate.md>) - the input this result scores
- [`EvaluateResult`](<https://docs.dhtmlx.com/planning-engine/net/api/core/evaluate/evaluate-result.md>) - the call's result that holds one `CandidateResult` per candidate
- [`Metrics`](<https://docs.dhtmlx.com/planning-engine/net/api/core/evaluate/metrics.md>) - the stage metrics and how `Cost` is computed
- [Evaluating alternatives](<https://docs.dhtmlx.com/planning-engine/net/resources-and-assignment/evaluating-alternatives.md#failed-and-cyclic-candidates>) - ranking candidates and handling failed ones
